1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
// This file is part of Substrate.
// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Common traits and types that are useful for describing offences for usage in environments
//! that use staking.
use sp_std::vec::Vec;
use codec::{Decode, Encode};
use sp_runtime::Perbill;
use crate::SessionIndex;
/// The kind of an offence, is a byte string representing some kind identifier
/// e.g. `b"im-online:offlin"`, `b"babe:equivocatio"`
pub type Kind = [u8; 16];
/// Number of times the offence of this authority was already reported in the past.
///
/// Note that we don't buffer offence reporting, so every time we see a new offence
/// of the same kind, we will report past authorities again.
/// This counter keeps track of how many times the authority was already reported in the past,
/// so that we can slash it accordingly.
pub type OffenceCount = u32;
/// In case of an offence, which conditions get an offending validator disabled.
#[derive(
Clone,
Copy,
PartialEq,
Eq,
Hash,
PartialOrd,
Ord,
Encode,
Decode,
sp_runtime::RuntimeDebug,
scale_info::TypeInfo,
)]
pub enum DisableStrategy {
/// Independently of slashing, this offence will not disable the offender.
Never,
/// Only disable the offender if it is also slashed.
WhenSlashed,
/// Independently of slashing, this offence will always disable the offender.
Always,
}
/// A trait implemented by an offence report.
///
/// This trait assumes that the offence is legitimate and was validated already.
///
/// Examples of offences include: a BABE equivocation or a GRANDPA unjustified vote.
pub trait Offence<Offender> {
/// Identifier which is unique for this kind of an offence.
const ID: Kind;
/// A type that represents a point in time on an abstract timescale.
///
/// See `Offence::time_slot` for details. The only requirement is that such timescale could be
/// represented by a single `u128` value.
type TimeSlot: Clone + codec::Codec + Ord;
/// The list of all offenders involved in this incident.
///
/// The list has no duplicates, so it is rather a set.
fn offenders(&self) -> Vec<Offender>;
/// The session index that is used for querying the validator set for the `slash_fraction`
/// function.
///
/// This is used for filtering historical sessions.
fn session_index(&self) -> SessionIndex;
/// Return a validator set count at the time when the offence took place.
fn validator_set_count(&self) -> u32;
/// A point in time when this offence happened.
///
/// This is used for looking up offences that happened at the "same time".
///
/// The timescale is abstract and doesn't have to be the same across different implementations
/// of this trait. The value doesn't represent absolute timescale though since it is interpreted
/// along with the `session_index`. Two offences are considered to happen at the same time iff
/// both `session_index` and `time_slot` are equal.
///
/// As an example, for GRANDPA timescale could be a round number and for BABE it could be a slot
/// number. Note that for GRANDPA the round number is reset each epoch.
fn time_slot(&self) -> Self::TimeSlot;
/// In which cases this offence needs to disable offenders until the next era starts.
fn disable_strategy(&self) -> DisableStrategy {
DisableStrategy::WhenSlashed
}
/// A slash fraction of the total exposure that should be slashed for this
/// particular offence for the `offenders_count` that happened at a singular `TimeSlot`.
///
/// `offenders_count` - the count of unique offending authorities for this `TimeSlot`. It is >0.
fn slash_fraction(&self, offenders_count: u32) -> Perbill;
}
/// Errors that may happen on offence reports.
#[derive(PartialEq, sp_runtime::RuntimeDebug)]
pub enum OffenceError {
/// The report has already been sumbmitted.
DuplicateReport,
/// Other error has happened.
Other(u8),
}
impl sp_runtime::traits::Printable for OffenceError {
fn print(&self) {
"OffenceError".print();
match self {
Self::DuplicateReport => "DuplicateReport".print(),
Self::Other(e) => {
"Other".print();
e.print();
},
}
}
}
/// A trait for decoupling offence reporters from the actual handling of offence reports.
pub trait ReportOffence<Reporter, Offender, O: Offence<Offender>> {
/// Report an `offence` and reward given `reporters`.
fn report_offence(reporters: Vec<Reporter>, offence: O) -> Result<(), OffenceError>;
/// Returns true iff all of the given offenders have been previously reported
/// at the given time slot. This function is useful to prevent the sending of
/// duplicate offence reports.
fn is_known_offence(offenders: &[Offender], time_slot: &O::TimeSlot) -> bool;
}
impl<Reporter, Offender, O: Offence<Offender>> ReportOffence<Reporter, Offender, O> for () {
fn report_offence(_reporters: Vec<Reporter>, _offence: O) -> Result<(), OffenceError> {
Ok(())
}
fn is_known_offence(_offenders: &[Offender], _time_slot: &O::TimeSlot) -> bool {
true
}
}
/// A trait to take action on an offence.
///
/// Used to decouple the module that handles offences and
/// the one that should punish for those offences.
pub trait OnOffenceHandler<Reporter, Offender, Res> {
/// A handler for an offence of a particular kind.
///
/// Note that this contains a list of all previous offenders
/// as well. The implementer should cater for a case, where
/// the same authorities were reported for the same offence
/// in the past (see `OffenceCount`).
///
/// The vector of `slash_fraction` contains `Perbill`s
/// the authorities should be slashed and is computed
/// according to the `OffenceCount` already. This is of the same length as `offenders.`
/// Zero is a valid value for a fraction.
///
/// The `session` parameter is the session index of the offence.
///
/// The `disable_strategy` parameter decides if the offenders need to be disabled immediately.
///
/// The receiver might decide to not accept this offence. In this case, the call site is
/// responsible for queuing the report and re-submitting again.
fn on_offence(
offenders: &[OffenceDetails<Reporter, Offender>],
slash_fraction: &[Perbill],
session: SessionIndex,
disable_strategy: DisableStrategy,
) -> Res;
}
impl<Reporter, Offender, Res: Default> OnOffenceHandler<Reporter, Offender, Res> for () {
fn on_offence(
_offenders: &[OffenceDetails<Reporter, Offender>],
_slash_fraction: &[Perbill],
_session: SessionIndex,
_disable_strategy: DisableStrategy,
) -> Res {
Default::default()
}
}
/// A details about an offending authority for a particular kind of offence.
#[derive(Clone, PartialEq, Eq, Encode, Decode, sp_runtime::RuntimeDebug, scale_info::TypeInfo)]
pub struct OffenceDetails<Reporter, Offender> {
/// The offending authority id
pub offender: Offender,
/// A list of reporters of offences of this authority ID. Possibly empty where there are no
/// particular reporters.
pub reporters: Vec<Reporter>,
}