referrerpolicy=no-referrer-when-downgrade

pallet_root_offences/
lib.rs

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//! # Root Offences Pallet
19//! Pallet that allows the root to create an offence.
20//!
21//! NOTE: This pallet should be used for testing purposes.
22
23#![cfg_attr(not(feature = "std"), no_std)]
24
25#[cfg(test)]
26mod mock;
27#[cfg(test)]
28mod tests;
29
30extern crate alloc;
31
32use alloc::vec::Vec;
33pub use pallet::*;
34use pallet_session::historical::IdentificationTuple;
35use sp_runtime::{traits::Convert, Perbill};
36use sp_staking::offence::OnOffenceHandler;
37
38#[frame_support::pallet]
39pub mod pallet {
40	use super::*;
41	use frame_support::pallet_prelude::*;
42	use frame_system::pallet_prelude::*;
43	use sp_staking::SessionIndex;
44
45	#[pallet::config]
46	pub trait Config:
47		frame_system::Config
48		+ pallet_staking::Config
49		+ pallet_session::Config<ValidatorId = <Self as frame_system::Config>::AccountId>
50		+ pallet_session::historical::Config
51	{
52		#[allow(deprecated)]
53		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
54		/// The offence handler provided by the runtime.
55		type OffenceHandler: OnOffenceHandler<Self::AccountId, IdentificationTuple<Self>, Weight>;
56	}
57
58	#[pallet::pallet]
59	pub struct Pallet<T>(_);
60
61	#[pallet::event]
62	#[pallet::generate_deposit(pub(super) fn deposit_event)]
63	pub enum Event<T: Config> {
64		/// An offence was created by root.
65		OffenceCreated { offenders: Vec<(T::AccountId, Perbill)> },
66	}
67
68	#[pallet::error]
69	pub enum Error<T> {
70		/// Failed to get the active era from the staking pallet.
71		FailedToGetActiveEra,
72	}
73
74	type OffenceDetails<T> = sp_staking::offence::OffenceDetails<
75		<T as frame_system::Config>::AccountId,
76		IdentificationTuple<T>,
77	>;
78
79	#[pallet::call]
80	impl<T: Config> Pallet<T> {
81		/// Allows the `root`, for example sudo to create an offence.
82		///
83		/// If `identifications` is `Some`, then the given identification is used for offence. Else,
84		/// it is fetched live from `session::Historical`.
85		#[pallet::call_index(0)]
86		#[pallet::weight(T::DbWeight::get().reads(2))]
87		pub fn create_offence(
88			origin: OriginFor<T>,
89			offenders: Vec<(T::AccountId, Perbill)>,
90			maybe_identifications: Option<Vec<T::FullIdentification>>,
91			maybe_session_index: Option<SessionIndex>,
92		) -> DispatchResult {
93			ensure_root(origin)?;
94
95			ensure!(
96				maybe_identifications.as_ref().map_or(true, |ids| ids.len() == offenders.len()),
97				"InvalidIdentificationLength"
98			);
99
100			let identifications =
101				maybe_identifications.ok_or("Unreachable-NoIdentification").or_else(|_| {
102					offenders
103						.iter()
104						.map(|(who, _)| {
105							T::FullIdentificationOf::convert(who.clone())
106								.ok_or("failed to call FullIdentificationOf")
107						})
108						.collect::<Result<Vec<_>, _>>()
109				})?;
110
111			let slash_fraction =
112				offenders.clone().into_iter().map(|(_, fraction)| fraction).collect::<Vec<_>>();
113			let offence_details = Self::get_offence_details(offenders.clone(), identifications)?;
114
115			Self::submit_offence(&offence_details, &slash_fraction, maybe_session_index);
116			Self::deposit_event(Event::OffenceCreated { offenders });
117			Ok(())
118		}
119	}
120
121	impl<T: Config> Pallet<T> {
122		/// Returns a vector of offenders that are going to be slashed.
123		fn get_offence_details(
124			offenders: Vec<(T::AccountId, Perbill)>,
125			identifications: Vec<T::FullIdentification>,
126		) -> Result<Vec<OffenceDetails<T>>, DispatchError> {
127			Ok(offenders
128				.clone()
129				.into_iter()
130				.zip(identifications.into_iter())
131				.map(|((o, _), i)| OffenceDetails::<T> {
132					offender: (o.clone(), i),
133					reporters: Default::default(),
134				})
135				.collect())
136		}
137
138		/// Submits the offence by calling the `on_offence` function.
139		fn submit_offence(
140			offenders: &[OffenceDetails<T>],
141			slash_fraction: &[Perbill],
142			maybe_session_index: Option<SessionIndex>,
143		) {
144			let session_index = maybe_session_index.unwrap_or_else(|| {
145				<pallet_session::Pallet<T> as frame_support::traits::ValidatorSet<
146						T::AccountId,
147					>>::session_index()
148			});
149			T::OffenceHandler::on_offence(&offenders, &slash_fraction, session_index);
150		}
151	}
152}