mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-25 01:15:47 +00:00
Offences reporting and slashing (#3322)
* Remove offline slashing logic from staking. * Initial version of reworked offence module, can report offences * Clean up staking example. * Commit SlashingOffence * Force new era on slash. * Add offenders in the SlashingOffence trait. * Introduce the ReportOffence trait. * Rename `Offence`. * Add on_before_session_ending handler. * Move offence related stuff under sr-primitives. * Fix cargo check. * Import new im-online implementation. * Adding validator count to historical session storage as it's needed for slash calculations * Add a comment about offence. * Add BabeEquivocationOffence * GrandpaEquivocationOffence * slash_fraction and fix * current_era_start_session_index * UnresponsivnessOffence * Finalise OnOffenceHandler traits, and stub impl for staking. * slash_fraction doesn't really need &self * Note that offenders count is greater than 0 * Add a test to ensure that I got the math right * Use FullIdentification in offences. * Use FullIndentification. * Hook up the offences module. * Report unresponsive validators * Make sure eras have the same length. * Slashing and rewards. * Fix compilation. * Distribute rewards. * Supply validators_count * Use identificationTuple in Unresponsivness report * Fix merge. * Make sure we don't slash if amount is zero. * We don't return an error from report_offence anymo * We actually can use vec! * Prevent division by zero if the reporters is empty * offence_forces_new_era/nominators_also_get_slashed * advance_session * Fix tests. * Update srml/staking/src/lib.rs Co-Authored-By: Robert Habermeier <rphmeier@gmail.com> * slashing_performed_according_exposure * Check that reporters receive their slice. * Small clean-up. * invulnerables_are_not_slashed * Minor clean ups. * Improve docs. * dont_slash_if_fraction_is_zero * Remove session dependency from offences. * Introduce sr-staking-primitives * Move offence under sr_staking_primitives * rename session_index * Resolves todos re using SessionIndex * Fix staking tests. * Properly scale denominator. * Fix UnresponsivnessOffence * Fix compilation. * Tests for offences. * Clean offences tests. * Fix staking doc test. * Bump spec version * Fix aura tests. * Fix node_executor * Deposit an event on offence. * Fix compilation of node-runtime * Remove aura slashing logic. * Remove HandleReport * Update docs for timeslot. * rename with_on_offence_fractions * Add should_properly_count_offences * Replace ValidatorIdByIndex with CurrentElectedSet ValidatorIdByIndex was querying the current_elected set in each call, doing loading (even though its from cache), deserializing and cloning of element. Instead of this it is more efficient to use `CurrentElectedSet`. As a small bonus, the invariant became a little bit easier: now we just rely on the fact that `keys` and `current_elected` set are of the same length rather than relying on the fact that `validator_id_by_index` would work similar to `<[T]>::get`. * Clarify babe equivocation * Fix offences. * Rename validators_count to validator_set_count * Fix squaring. * Update core/sr-staking-primitives/src/offence.rs Co-Authored-By: Gavin Wood <gavin@parity.io> * Docs for CurrentElectedSet. * Don't punish only invulnerables * Use `get/insert` instead of `mutate`. * Fix compilation * Update core/sr-staking-primitives/src/offence.rs Co-Authored-By: Gavin Wood <gavin@parity.io> * Update srml/offences/src/lib.rs Co-Authored-By: Robert Habermeier <rphmeier@gmail.com> * Update srml/im-online/src/lib.rs Co-Authored-By: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Update srml/im-online/src/lib.rs Co-Authored-By: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Update srml/im-online/src/lib.rs Co-Authored-By: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Update srml/babe/src/lib.rs Co-Authored-By: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Update core/sr-staking-primitives/src/offence.rs Co-Authored-By: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Update core/sr-staking-primitives/src/offence.rs Co-Authored-By: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Update core/sr-staking-primitives/src/offence.rs Co-Authored-By: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Update core/sr-staking-primitives/src/offence.rs Co-Authored-By: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Update core/sr-staking-primitives/src/offence.rs Co-Authored-By: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Add aura todo. * Allow multiple reports for single offence report. * Fix slash_fraction calculation. * Fix typos. * Fix compilation and tests. * Fix staking tests. * Update srml/im-online/src/lib.rs Co-Authored-By: Logan Saether <x@logansaether.com> * Fix doc on time_slot * Allow slashing only on current era (#3411) * only slash in current era * prune journal for last era * comment own_slash * emit an event when old slashing events are discarded * Pave the way for pruning * Address issues. * Try to refactor collect_offence_reports * Other fixes. * More fixes.
This commit is contained in:
committed by
Gavin Wood
parent
99f3f07690
commit
6cc4495700
@@ -31,9 +31,6 @@
|
||||
//!
|
||||
//! ## Related Modules
|
||||
//!
|
||||
//! - [Staking](../srml_staking/index.html): The Staking module is called in Aura to enforce slashing
|
||||
//! if validators miss a certain number of slots (see the [`StakingSlasher`](./struct.StakingSlasher.html)
|
||||
//! struct and associated method).
|
||||
//! - [Timestamp](../srml_timestamp/index.html): The Timestamp module is used in Aura to track
|
||||
//! consensus rounds (via `slots`).
|
||||
//! - [Consensus](../srml_consensus/index.html): The Consensus module does not relate directly to Aura,
|
||||
@@ -55,7 +52,7 @@ use codec::Encode;
|
||||
use srml_support::{decl_storage, decl_module, Parameter, storage::StorageValue, traits::Get};
|
||||
use app_crypto::AppPublic;
|
||||
use sr_primitives::{
|
||||
traits::{SaturatedConversion, Saturating, Zero, One, Member, IsMember}, generic::DigestItem,
|
||||
traits::{SaturatedConversion, Saturating, Zero, Member, IsMember}, generic::DigestItem,
|
||||
};
|
||||
use timestamp::OnTimestampSet;
|
||||
#[cfg(feature = "std")]
|
||||
@@ -142,19 +139,7 @@ impl ProvideInherentData for InherentDataProvider {
|
||||
}
|
||||
}
|
||||
|
||||
/// Something that can handle Aura consensus reports.
|
||||
pub trait HandleReport {
|
||||
fn handle_report(report: AuraReport);
|
||||
}
|
||||
|
||||
impl HandleReport for () {
|
||||
fn handle_report(_report: AuraReport) { }
|
||||
}
|
||||
|
||||
pub trait Trait: timestamp::Trait {
|
||||
/// The logic for handling reports.
|
||||
type HandleReport: HandleReport;
|
||||
|
||||
/// The identifier type for an authority.
|
||||
type AuthorityId: Member + Parameter + AppPublic + Default;
|
||||
}
|
||||
@@ -245,34 +230,6 @@ impl<T: Trait> IsMember<T::AuthorityId> for Module<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A report of skipped authorities in Aura.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
pub struct AuraReport {
|
||||
// The first skipped slot.
|
||||
start_slot: usize,
|
||||
// The number of times authorities were skipped.
|
||||
skipped: usize,
|
||||
}
|
||||
|
||||
impl AuraReport {
|
||||
/// Call the closure with (`validator_indices`, `punishment_count`) for each
|
||||
/// validator to punish.
|
||||
pub fn punish<F>(&self, validator_count: usize, mut punish_with: F)
|
||||
where F: FnMut(usize, usize)
|
||||
{
|
||||
// If all validators have been skipped, then it implies some sort of
|
||||
// systematic problem common to all rather than a minority of validators
|
||||
// not fulfilling their specific duties. In this case, it doesn't make
|
||||
// sense to punish anyone, so we guard against it.
|
||||
if self.skipped < validator_count {
|
||||
for index in 0..self.skipped {
|
||||
punish_with((self.start_slot + index) % validator_count, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> Module<T> {
|
||||
/// Determine the Aura slot-duration based on the Timestamp module configuration.
|
||||
pub fn slot_duration() -> T::Moment {
|
||||
@@ -281,7 +238,7 @@ impl<T: Trait> Module<T> {
|
||||
<T as timestamp::Trait>::MinimumPeriod::get().saturating_mul(2.into())
|
||||
}
|
||||
|
||||
fn on_timestamp_set<H: HandleReport>(now: T::Moment, slot_duration: T::Moment) {
|
||||
fn on_timestamp_set(now: T::Moment, slot_duration: T::Moment) {
|
||||
let last = Self::last();
|
||||
<Self as Store>::LastTimestamp::put(now.clone());
|
||||
|
||||
@@ -292,42 +249,17 @@ impl<T: Trait> Module<T> {
|
||||
assert!(!slot_duration.is_zero(), "Aura slot duration cannot be zero.");
|
||||
|
||||
let last_slot = last / slot_duration.clone();
|
||||
let first_skipped = last_slot.clone() + One::one();
|
||||
let cur_slot = now / slot_duration;
|
||||
|
||||
assert!(last_slot < cur_slot, "Only one block may be authored per slot.");
|
||||
if cur_slot == first_skipped { return }
|
||||
|
||||
let skipped_slots = cur_slot - last_slot - One::one();
|
||||
|
||||
H::handle_report(AuraReport {
|
||||
start_slot: first_skipped.saturated_into::<usize>(),
|
||||
skipped: skipped_slots.saturated_into::<usize>(),
|
||||
})
|
||||
// TODO [#3398] Generate offence report for all authorities that skipped their slots.
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> OnTimestampSet<T::Moment> for Module<T> {
|
||||
fn on_timestamp_set(moment: T::Moment) {
|
||||
Self::on_timestamp_set::<T::HandleReport>(moment, Self::slot_duration())
|
||||
}
|
||||
}
|
||||
|
||||
/// A type for performing slashing based on Aura reports.
|
||||
pub struct StakingSlasher<T>(::rstd::marker::PhantomData<T>);
|
||||
|
||||
impl<T: staking::Trait + Trait> HandleReport for StakingSlasher<T> {
|
||||
fn handle_report(report: AuraReport) {
|
||||
use staking::SessionInterface;
|
||||
let validators = T::SessionInterface::validators();
|
||||
|
||||
report.punish(
|
||||
validators.len(),
|
||||
|idx, slash_count| {
|
||||
let v = validators[idx].clone();
|
||||
staking::Module::<T>::on_offline_validator(v, slash_count);
|
||||
}
|
||||
);
|
||||
Self::on_timestamp_set(moment, Self::slot_duration())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,6 @@ impl timestamp::Trait for Test {
|
||||
}
|
||||
|
||||
impl Trait for Test {
|
||||
type HandleReport = ();
|
||||
type AuthorityId = AuthorityId;
|
||||
}
|
||||
|
||||
@@ -81,5 +80,4 @@ pub fn new_test_ext(authorities: Vec<u64>) -> runtime_io::TestExternalities<Blak
|
||||
t.into()
|
||||
}
|
||||
|
||||
pub type System = system::Module<Test>;
|
||||
pub type Aura = Module<Test>;
|
||||
|
||||
@@ -18,75 +18,13 @@
|
||||
|
||||
#![cfg(test)]
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use crate::mock::{System, Aura, new_test_ext};
|
||||
use sr_primitives::traits::Header;
|
||||
use runtime_io::with_externalities;
|
||||
use parking_lot::Mutex;
|
||||
use crate::{AuraReport, HandleReport};
|
||||
use crate::mock::{Aura, new_test_ext};
|
||||
|
||||
#[test]
|
||||
fn aura_report_gets_skipped_correctly() {
|
||||
let mut report = AuraReport {
|
||||
start_slot: 3,
|
||||
skipped: 15,
|
||||
};
|
||||
|
||||
let mut validators = vec![0; 10];
|
||||
report.punish(10, |idx, count| validators[idx] += count);
|
||||
assert_eq!(validators, vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
|
||||
let mut validators = vec![0; 10];
|
||||
report.skipped = 5;
|
||||
report.punish(10, |idx, count| validators[idx] += count);
|
||||
assert_eq!(validators, vec![0, 0, 0, 1, 1, 1, 1, 1, 0, 0]);
|
||||
|
||||
let mut validators = vec![0; 10];
|
||||
report.start_slot = 8;
|
||||
report.punish(10, |idx, count| validators[idx] += count);
|
||||
assert_eq!(validators, vec![1, 1, 1, 0, 0, 0, 0, 0, 1, 1]);
|
||||
|
||||
let mut validators = vec![0; 4];
|
||||
report.start_slot = 1;
|
||||
report.skipped = 3;
|
||||
report.punish(4, |idx, count| validators[idx] += count);
|
||||
assert_eq!(validators, vec![0, 1, 1, 1]);
|
||||
|
||||
let mut validators = vec![0; 4];
|
||||
report.start_slot = 2;
|
||||
report.punish(4, |idx, count| validators[idx] += count);
|
||||
assert_eq!(validators, vec![1, 0, 1, 1]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aura_reports_offline() {
|
||||
lazy_static! {
|
||||
static ref SLASH_COUNTS: Mutex<Vec<usize>> = Mutex::new(vec![0; 4]);
|
||||
}
|
||||
|
||||
struct HandleTestReport;
|
||||
impl HandleReport for HandleTestReport {
|
||||
fn handle_report(report: AuraReport) {
|
||||
let mut counts = SLASH_COUNTS.lock();
|
||||
report.punish(counts.len(), |idx, count| counts[idx] += count);
|
||||
}
|
||||
}
|
||||
|
||||
fn initial_values() {
|
||||
with_externalities(&mut new_test_ext(vec![0, 1, 2, 3]), || {
|
||||
System::initialize(&1, &Default::default(), &Default::default(), &Default::default());
|
||||
let slot_duration = Aura::slot_duration();
|
||||
|
||||
Aura::on_timestamp_set::<HandleTestReport>(5 * slot_duration, slot_duration);
|
||||
let header = System::finalize();
|
||||
|
||||
// no slashing when last step was 0.
|
||||
assert_eq!(SLASH_COUNTS.lock().as_slice(), &[0, 0, 0, 0]);
|
||||
|
||||
System::initialize(&2, &header.hash(), &Default::default(), &Default::default());
|
||||
Aura::on_timestamp_set::<HandleTestReport>(8 * slot_duration, slot_duration);
|
||||
let _header = System::finalize();
|
||||
|
||||
// Steps 6 and 7 were skipped.
|
||||
assert_eq!(SLASH_COUNTS.lock().as_slice(), &[0, 0, 1, 1]);
|
||||
assert_eq!(Aura::last(), 0u64);
|
||||
assert_eq!(Aura::authorities().len(), 4);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user