1
// This file is part of Substrate.
2

            
3
// Copyright (C) Parity Technologies (UK) Ltd.
4
// SPDX-License-Identifier: Apache-2.0
5

            
6
// Licensed under the Apache License, Version 2.0 (the "License");
7
// you may not use this file except in compliance with the License.
8
// You may obtain a copy of the License at
9
//
10
// 	http://www.apache.org/licenses/LICENSE-2.0
11
//
12
// Unless required by applicable law or agreed to in writing, software
13
// distributed under the License is distributed on an "AS IS" BASIS,
14
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
// See the License for the specific language governing permissions and
16
// limitations under the License.
17

            
18
//! Common traits and types that are useful for describing offences for usage in environments
19
//! that use staking.
20

            
21
use alloc::vec::Vec;
22
use codec::{Decode, Encode};
23
use sp_core::Get;
24
use sp_runtime::{transaction_validity::TransactionValidityError, DispatchError, Perbill};
25

            
26
use crate::SessionIndex;
27

            
28
/// The kind of an offence, is a byte string representing some kind identifier
29
/// e.g. `b"im-online:offlin"`, `b"babe:equivocatio"`
30
pub type Kind = [u8; 16];
31

            
32
/// Number of times the offence of this authority was already reported in the past.
33
///
34
/// Note that we don't buffer offence reporting, so every time we see a new offence
35
/// of the same kind, we will report past authorities again.
36
/// This counter keeps track of how many times the authority was already reported in the past,
37
/// so that we can slash it accordingly.
38
pub type OffenceCount = u32;
39

            
40
/// A trait implemented by an offence report.
41
///
42
/// This trait assumes that the offence is legitimate and was validated already.
43
///
44
/// Examples of offences include: a BABE equivocation or a GRANDPA unjustified vote.
45
pub trait Offence<Offender> {
46
	/// Identifier which is unique for this kind of an offence.
47
	const ID: Kind;
48

            
49
	/// A type that represents a point in time on an abstract timescale.
50
	///
51
	/// See `Offence::time_slot` for details. The only requirement is that such timescale could be
52
	/// represented by a single `u128` value.
53
	type TimeSlot: Clone + codec::Codec + Ord;
54

            
55
	/// The list of all offenders involved in this incident.
56
	///
57
	/// The list has no duplicates, so it is rather a set.
58
	fn offenders(&self) -> Vec<Offender>;
59

            
60
	/// The session index that is used for querying the validator set for the `slash_fraction`
61
	/// function.
62
	///
63
	/// This is used for filtering historical sessions.
64
	fn session_index(&self) -> SessionIndex;
65

            
66
	/// Return a validator set count at the time when the offence took place.
67
	fn validator_set_count(&self) -> u32;
68

            
69
	/// A point in time when this offence happened.
70
	///
71
	/// This is used for looking up offences that happened at the "same time".
72
	///
73
	/// The timescale is abstract and doesn't have to be the same across different implementations
74
	/// of this trait. The value doesn't represent absolute timescale though since it is interpreted
75
	/// along with the `session_index`. Two offences are considered to happen at the same time iff
76
	/// both `session_index` and `time_slot` are equal.
77
	///
78
	/// As an example, for GRANDPA timescale could be a round number and for BABE it could be a slot
79
	/// number. Note that for GRANDPA the round number is reset each epoch.
80
	fn time_slot(&self) -> Self::TimeSlot;
81

            
82
	/// A slash fraction of the total exposure that should be slashed for this
83
	/// particular offence for the `offenders_count` that happened at a singular `TimeSlot`.
84
	///
85
	/// `offenders_count` - the count of unique offending authorities for this `TimeSlot`. It is >0.
86
	fn slash_fraction(&self, offenders_count: u32) -> Perbill;
87
}
88

            
89
/// Errors that may happen on offence reports.
90
#[derive(PartialEq, sp_runtime::RuntimeDebug)]
91
pub enum OffenceError {
92
	/// The report has already been submitted.
93
	DuplicateReport,
94

            
95
	/// Other error has happened.
96
	Other(u8),
97
}
98

            
99
impl sp_runtime::traits::Printable for OffenceError {
100
	fn print(&self) {
101
		"OffenceError".print();
102
		match self {
103
			Self::DuplicateReport => "DuplicateReport".print(),
104
			Self::Other(e) => {
105
				"Other".print();
106
				e.print();
107
			},
108
		}
109
	}
110
}
111

            
112
/// A trait for decoupling offence reporters from the actual handling of offence reports.
113
pub trait ReportOffence<Reporter, Offender, O: Offence<Offender>> {
114
	/// Report an `offence` and reward given `reporters`.
115
	fn report_offence(reporters: Vec<Reporter>, offence: O) -> Result<(), OffenceError>;
116

            
117
	/// Returns true iff all of the given offenders have been previously reported
118
	/// at the given time slot. This function is useful to prevent the sending of
119
	/// duplicate offence reports.
120
	fn is_known_offence(offenders: &[Offender], time_slot: &O::TimeSlot) -> bool;
121
}
122

            
123
impl<Reporter, Offender, O: Offence<Offender>> ReportOffence<Reporter, Offender, O> for () {
124
	fn report_offence(_reporters: Vec<Reporter>, _offence: O) -> Result<(), OffenceError> {
125
		Ok(())
126
	}
127

            
128
	fn is_known_offence(_offenders: &[Offender], _time_slot: &O::TimeSlot) -> bool {
129
		true
130
	}
131
}
132

            
133
/// A trait to take action on an offence.
134
///
135
/// Used to decouple the module that handles offences and
136
/// the one that should punish for those offences.
137
pub trait OnOffenceHandler<Reporter, Offender, Res> {
138
	/// A handler for an offence of a particular kind.
139
	///
140
	/// Note that this contains a list of all previous offenders
141
	/// as well. The implementer should cater for a case, where
142
	/// the same authorities were reported for the same offence
143
	/// in the past (see `OffenceCount`).
144
	///
145
	/// The vector of `slash_fraction` contains `Perbill`s
146
	/// the authorities should be slashed and is computed
147
	/// according to the `OffenceCount` already. This is of the same length as `offenders.`
148
	/// Zero is a valid value for a fraction.
149
	///
150
	/// The `session` parameter is the session index of the offence.
151
	///
152
	/// The receiver might decide to not accept this offence. In this case, the call site is
153
	/// responsible for queuing the report and re-submitting again.
154
	fn on_offence(
155
		offenders: &[OffenceDetails<Reporter, Offender>],
156
		slash_fraction: &[Perbill],
157
		session: SessionIndex,
158
	) -> Res;
159
}
160

            
161
impl<Reporter, Offender, Res: Default> OnOffenceHandler<Reporter, Offender, Res> for () {
162
	fn on_offence(
163
		_offenders: &[OffenceDetails<Reporter, Offender>],
164
		_slash_fraction: &[Perbill],
165
		_session: SessionIndex,
166
	) -> Res {
167
		Default::default()
168
	}
169
}
170

            
171
/// A details about an offending authority for a particular kind of offence.
172
#[derive(Clone, PartialEq, Eq, Encode, Decode, sp_runtime::RuntimeDebug, scale_info::TypeInfo)]
173
pub struct OffenceDetails<Reporter, Offender> {
174
	/// The offending authority id
175
	pub offender: Offender,
176
	/// A list of reporters of offences of this authority ID. Possibly empty where there are no
177
	/// particular reporters.
178
	pub reporters: Vec<Reporter>,
179
}
180

            
181
/// An abstract system to publish, check and process offence evidences.
182
///
183
/// Implementation details are left opaque and we don't assume any specific usage
184
/// scenario for this trait at this level. The main goal is to group together some
185
/// common actions required during a typical offence report flow.
186
///
187
/// Even though this trait doesn't assume too much, this is a general guideline
188
/// for a typical usage scenario:
189
///
190
/// 1. An offence is detected and an evidence is submitted on-chain via the
191
///    [`OffenceReportSystem::publish_evidence`] method. This will construct and submit an extrinsic
192
///    transaction containing the offence evidence.
193
///
194
/// 2. If the extrinsic is unsigned then the transaction receiver may want to perform some
195
///    preliminary checks before further processing. This is a good place to call the
196
///    [`OffenceReportSystem::check_evidence`] method.
197
///
198
/// 3. Finally the report extrinsic is executed on-chain. This is where the user calls the
199
///    [`OffenceReportSystem::process_evidence`] to consume the offence report and enact any
200
///    required action.
201
pub trait OffenceReportSystem<Reporter, Evidence> {
202
	/// Longevity, in blocks, for the evidence report validity.
203
	///
204
	/// For example, when using the staking pallet this should be set equal
205
	/// to the bonding duration in blocks, not eras.
206
	type Longevity: Get<u64>;
207

            
208
	/// Publish an offence evidence.
209
	///
210
	/// Common usage: submit the evidence on-chain via some kind of extrinsic.
211
	fn publish_evidence(evidence: Evidence) -> Result<(), ()>;
212

            
213
	/// Check an offence evidence.
214
	///
215
	/// Common usage: preliminary validity check before execution
216
	/// (e.g. for unsigned extrinsic quick checks).
217
	fn check_evidence(evidence: Evidence) -> Result<(), TransactionValidityError>;
218

            
219
	/// Process an offence evidence.
220
	///
221
	/// Common usage: enact some form of slashing directly or by forwarding
222
	/// the evidence to a lower level specialized subsystem (e.g. a handler
223
	/// implementing `ReportOffence` trait).
224
	fn process_evidence(reporter: Reporter, evidence: Evidence) -> Result<(), DispatchError>;
225
}
226

            
227
/// Dummy offence report system.
228
///
229
/// Doesn't do anything special and returns `Ok(())` for all the actions.
230
impl<Reporter, Evidence> OffenceReportSystem<Reporter, Evidence> for () {
231
	type Longevity = ();
232

            
233
	fn publish_evidence(_evidence: Evidence) -> Result<(), ()> {
234
		Ok(())
235
	}
236

            
237
	fn check_evidence(_evidence: Evidence) -> Result<(), TransactionValidityError> {
238
		Ok(())
239
	}
240

            
241
	fn process_evidence(_reporter: Reporter, _evidence: Evidence) -> Result<(), DispatchError> {
242
		Ok(())
243
	}
244
}