// This file is part of Substrate. // Copyright (C) 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. //! # Root Offences Pallet //! Pallet that allows the root to create an offence. //! //! NOTE: This pallet should be used for testing purposes. #![cfg_attr(not(feature = "std"), no_std)] #[cfg(test)] mod mock; #[cfg(test)] mod tests; extern crate alloc; use alloc::vec::Vec; use pallet_session::historical::IdentificationTuple; use pallet_staking::{BalanceOf, Exposure, ExposureOf, Pallet as Staking}; use sp_runtime::Perbill; use sp_staking::offence::OnOffenceHandler; pub use pallet::*; #[frame_support::pallet] pub mod pallet { use super::*; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; #[pallet::config] pub trait Config: frame_system::Config + pallet_staking::Config + pallet_session::Config::AccountId> + pallet_session::historical::Config< FullIdentification = Exposure< ::AccountId, BalanceOf, >, FullIdentificationOf = ExposureOf, > { type RuntimeEvent: From> + IsType<::RuntimeEvent>; } #[pallet::pallet] pub struct Pallet(_); #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { /// An offence was created by root. OffenceCreated { offenders: Vec<(T::AccountId, Perbill)> }, } #[pallet::error] pub enum Error { /// Failed to get the active era from the staking pallet. FailedToGetActiveEra, } type OffenceDetails = sp_staking::offence::OffenceDetails< ::AccountId, IdentificationTuple, >; #[pallet::call] impl Pallet { /// Allows the `root`, for example sudo to create an offence. #[pallet::call_index(0)] #[pallet::weight(T::DbWeight::get().reads(2))] pub fn create_offence( origin: OriginFor, offenders: Vec<(T::AccountId, Perbill)>, ) -> DispatchResult { ensure_root(origin)?; let slash_fraction = offenders.clone().into_iter().map(|(_, fraction)| fraction).collect::>(); let offence_details = Self::get_offence_details(offenders.clone())?; Self::submit_offence(&offence_details, &slash_fraction); Self::deposit_event(Event::OffenceCreated { offenders }); Ok(()) } } impl Pallet { /// Returns a vector of offenders that are going to be slashed. fn get_offence_details( offenders: Vec<(T::AccountId, Perbill)>, ) -> Result>, DispatchError> { let now = Staking::::active_era() .map(|e| e.index) .ok_or(Error::::FailedToGetActiveEra)?; Ok(offenders .clone() .into_iter() .map(|(o, _)| OffenceDetails:: { offender: (o.clone(), Staking::::eras_stakers(now, &o)), reporters: Default::default(), }) .collect()) } /// Submits the offence by calling the `on_offence` function. fn submit_offence(offenders: &[OffenceDetails], slash_fraction: &[Perbill]) { let session_index = as frame_support::traits::ValidatorSet>::session_index(); as OnOffenceHandler< T::AccountId, IdentificationTuple, Weight, >>::on_offence(&offenders, &slash_fraction, session_index); } } }