[FRAME Core] New pallets: safe-mode and tx-pause (#12092)

* Add safe-mode

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Update pallet

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Add to kitchensink-runtime

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Spelling

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Rename to tx-pause

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Add SafeMode pallet

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* fmt

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Automatically disable safe-mode in on_init…

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Add permissionless enable+extend

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Add repay+slash stake methods

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Add docs

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fix stakes storage

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Genesis config for safe-mode pallet

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Genesis config for safe-mode pallet

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Rename ExtrinsicName to FunctionName

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Origin variable duration

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Rename FunctionName -> CallName

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Rename and docs

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Pallet safe mode tests (#12148)

* Add safe-mode mock runtime
* Add safe-mode tests
* Add ForceEnable- and ForceExtendOrigin
* Start dummy benchmarks
Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Tests for `pallet-tx-pause` (#12259)

* mock added
* tests added
* dummy benchmarks started

* rename to active/inactive
tests broken, in progress

* Runtime types, fix tests

* WIP safe mode and tx pause {continued} (#12371)

* test coverage on safe mode and tx pause
* benchmarks & tests
* tests passing, use FullNameOf to check tx-pause unfilterables
* naming updates

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Set block number

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* dummy weights generated, safe mode

* add repay_reservation call with RepaymentDelay per #10033 feature requirements

* make call name optional to allow pausing pallets, handle `Contains` correctly for this throughout, doc comments started

* move to full_name notation for all interfaces, last commit introduced 1 more storage read.

* refactor is_paused

* safe math on safe mode

* Make stuff compile

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Compile

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Cleanup & renames

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Cleanup TxPause pallet

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fix benches

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* fmt

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Refactor to fungibles::* and rename stuf

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Make compile

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fix node config

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Typos

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Remove CausalHoldReason

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Refactor benchmarks and runtime configs

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Add traits

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Remove old code

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Cleanup safe-mode benches

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Update frame/safe-mode/Cargo.toml

Co-authored-by: Liam Aharon <liam.aharon@hotmail.com>

* Update frame/safe-mode/Cargo.toml

Co-authored-by: Liam Aharon <liam.aharon@hotmail.com>

* Docs

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Remove getters

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Update Cargo.lock

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Remove phantom

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fix test

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Remove phantom

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Apply suggestions from code review

Co-authored-by: Liam Aharon <liam.aharon@hotmail.com>

* Use new as Origin benchmarking syntax

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Docs

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fix node

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fix tx-pause benches

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Renames

* Remove duplicate test

* Add docs

* docs

* Apply suggestions from code review

Co-authored-by: Liam Aharon <liam.aharon@hotmail.com>
Co-authored-by: Muharem Ismailov <ismailov.m.h@gmail.com>
Co-authored-by: Gonçalo Pestana <g6pestana@gmail.com>

* Cleanup tests

* docs

* Cleanup GenesisConfigs

* Doc traits

* Remove PauseTooLongNames

* docs

* Use V2 benchmarking

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Use RuntimeHoldReason

* Fix kitchensink runtime

* Fix CI

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fix CI

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Review

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Rename Stake to Deposit

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Add docs

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Add Notify and test it

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fix kitchensink

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Update frame/safe-mode/src/tests.rs

Co-authored-by: Liam Aharon <liam.aharon@hotmail.com>

* Update frame/safe-mode/src/tests.rs

Co-authored-by: Liam Aharon <liam.aharon@hotmail.com>

* Update frame/support/src/traits/safe_mode.rs

Co-authored-by: Liam Aharon <liam.aharon@hotmail.com>

* Update frame/support/src/traits/safe_mode.rs

Co-authored-by: Liam Aharon <liam.aharon@hotmail.com>

* Update frame/support/src/traits/safe_mode.rs

Co-authored-by: Liam Aharon <liam.aharon@hotmail.com>

* Update frame/support/src/traits/tx_pause.rs

Co-authored-by: Liam Aharon <liam.aharon@hotmail.com>

* Update frame/tx-pause/src/lib.rs

Co-authored-by: Liam Aharon <liam.aharon@hotmail.com>

* Update frame/tx-pause/src/lib.rs

Co-authored-by: Liam Aharon <liam.aharon@hotmail.com>

* Update frame/tx-pause/src/mock.rs

Co-authored-by: Liam Aharon <liam.aharon@hotmail.com>

* Update frame/support/src/traits/safe_mode.rs

Co-authored-by: Liam Aharon <liam.aharon@hotmail.com>

* Simplify code

* Update frame/support/src/traits/safe_mode.rs

Co-authored-by: Liam Aharon <liam.aharon@hotmail.com>

* Update frame/support/src/traits/safe_mode.rs

Co-authored-by: Liam Aharon <liam.aharon@hotmail.com>

* Update frame/support/src/traits/safe_mode.rs

Co-authored-by: Liam Aharon <liam.aharon@hotmail.com>

* Fixup merge

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Make stuff compile

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Make tx-pause compile again

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fix features

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fix more features

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* ".git/.scripts/commands/bench/bench.sh" --subcommand=pallet --runtime=dev --target_dir=substrate --pallet=pallet_safe_mode

* Update weights

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

---------

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Co-authored-by: Dan Shields <35669742+NukeManDan@users.noreply.github.com>
Co-authored-by: Dan Shields <nukemandan@protonmail.com>
Co-authored-by: Liam Aharon <liam.aharon@hotmail.com>
Co-authored-by: Muharem Ismailov <ismailov.m.h@gmail.com>
Co-authored-by: Gonçalo Pestana <g6pestana@gmail.com>
Co-authored-by: command-bot <>
This commit is contained in:
Oliver Tale-Yazdi
2023-08-25 02:56:00 +02:00
committed by GitHub
parent 46bd466e48
commit 3710edfedc
22 changed files with 3339 additions and 6 deletions
+277
View File
@@ -0,0 +1,277 @@
// This file is part of Substrate.
// Copyright (C) 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.
#![cfg_attr(not(feature = "std"), no_std)]
#![deny(rustdoc::broken_intra_doc_links)]
mod benchmarking;
pub mod mock;
mod tests;
pub mod weights;
use frame_support::{
dispatch::GetDispatchInfo,
pallet_prelude::*,
traits::{CallMetadata, Contains, GetCallMetadata, IsSubType, IsType},
DefaultNoBound,
};
use frame_system::pallet_prelude::*;
use sp_runtime::{traits::Dispatchable, DispatchResult};
use sp_std::{convert::TryInto, prelude::*};
pub use pallet::*;
pub use weights::*;
/// The stringy name of a pallet from [`GetCallMetadata`] for [`Config::RuntimeCall`] variants.
pub type PalletNameOf<T> = BoundedVec<u8, <T as Config>::MaxNameLen>;
/// The stringy name of a call (within a pallet) from [`GetCallMetadata`] for
/// [`Config::RuntimeCall`] variants.
pub type PalletCallNameOf<T> = BoundedVec<u8, <T as Config>::MaxNameLen>;
/// A fully specified pallet ([`PalletNameOf`]) and optional call ([`PalletCallNameOf`])
/// to partially or fully specify an item a variant of a [`Config::RuntimeCall`].
pub type RuntimeCallNameOf<T> = (PalletNameOf<T>, PalletCallNameOf<T>);
#[frame_support::pallet]
pub mod pallet {
use super::*;
#[pallet::pallet]
pub struct Pallet<T>(PhantomData<T>);
#[pallet::config]
pub trait Config: frame_system::Config {
/// The overarching event type.
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
/// The overarching call type.
type RuntimeCall: Parameter
+ Dispatchable<RuntimeOrigin = Self::RuntimeOrigin>
+ GetDispatchInfo
+ GetCallMetadata
+ From<frame_system::Call<Self>>
+ IsSubType<Call<Self>>
+ IsType<<Self as frame_system::Config>::RuntimeCall>;
/// The only origin that can pause calls.
type PauseOrigin: EnsureOrigin<Self::RuntimeOrigin>;
/// The only origin that can un-pause calls.
type UnpauseOrigin: EnsureOrigin<Self::RuntimeOrigin>;
/// Contains all calls that cannot be paused.
///
/// The `TxMode` pallet cannot pause its own calls, and does not need to be explicitly
/// added here.
type WhitelistedCalls: Contains<RuntimeCallNameOf<Self>>;
/// Maximum length for pallet name and call name SCALE encoded string names.
///
/// TOO LONG NAMES WILL BE TREATED AS PAUSED.
#[pallet::constant]
type MaxNameLen: Get<u32>;
// Weight information for extrinsics in this pallet.
type WeightInfo: WeightInfo;
}
/// The set of calls that are explicitly paused.
#[pallet::storage]
pub type PausedCalls<T: Config> =
StorageMap<_, Blake2_128Concat, RuntimeCallNameOf<T>, (), OptionQuery>;
#[pallet::error]
pub enum Error<T> {
/// The call is paused.
IsPaused,
/// The call is unpaused.
IsUnpaused,
/// The call is whitelisted and cannot be paused.
Unpausable,
// The pallet or call does not exist in the runtime.
NotFound,
}
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// This pallet, or a specific call is now paused.
CallPaused { full_name: RuntimeCallNameOf<T> },
/// This pallet, or a specific call is now unpaused.
CallUnpaused { full_name: RuntimeCallNameOf<T> },
}
/// Configure the initial state of this pallet in the genesis block.
#[pallet::genesis_config]
#[derive(DefaultNoBound)]
pub struct GenesisConfig<T: Config> {
/// Initially paused calls.
pub paused: Vec<RuntimeCallNameOf<T>>,
}
#[pallet::genesis_build]
impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
fn build(&self) {
for call in &self.paused {
Pallet::<T>::ensure_can_pause(&call).expect("Genesis data is known good; qed");
PausedCalls::<T>::insert(&call, ());
}
}
}
#[pallet::call]
impl<T: Config> Pallet<T> {
/// Pause a call.
///
/// Can only be called by [`Config::PauseOrigin`].
/// Emits an [`Event::CallPaused`] event on success.
#[pallet::call_index(0)]
#[pallet::weight(T::WeightInfo::pause())]
pub fn pause(origin: OriginFor<T>, full_name: RuntimeCallNameOf<T>) -> DispatchResult {
T::PauseOrigin::ensure_origin(origin)?;
Self::do_pause(full_name).map_err(Into::into)
}
/// Un-pause a call.
///
/// Can only be called by [`Config::UnpauseOrigin`].
/// Emits an [`Event::CallUnpaused`] event on success.
#[pallet::call_index(1)]
#[pallet::weight(T::WeightInfo::unpause())]
pub fn unpause(origin: OriginFor<T>, ident: RuntimeCallNameOf<T>) -> DispatchResult {
T::UnpauseOrigin::ensure_origin(origin)?;
Self::do_unpause(ident).map_err(Into::into)
}
}
}
impl<T: Config> Pallet<T> {
pub(crate) fn do_pause(ident: RuntimeCallNameOf<T>) -> Result<(), Error<T>> {
Self::ensure_can_pause(&ident)?;
PausedCalls::<T>::insert(&ident, ());
Self::deposit_event(Event::CallPaused { full_name: ident });
Ok(())
}
pub(crate) fn do_unpause(ident: RuntimeCallNameOf<T>) -> Result<(), Error<T>> {
Self::ensure_can_unpause(&ident)?;
PausedCalls::<T>::remove(&ident);
Self::deposit_event(Event::CallUnpaused { full_name: ident });
Ok(())
}
/// Return whether this call is paused.
pub fn is_paused(full_name: &RuntimeCallNameOf<T>) -> bool {
if T::WhitelistedCalls::contains(full_name) {
return false
}
<PausedCalls<T>>::contains_key(full_name)
}
/// Same as [`Self::is_paused`] but for inputs unbound by max-encoded-len.
pub fn is_paused_unbound(pallet: Vec<u8>, call: Vec<u8>) -> bool {
let pallet = PalletNameOf::<T>::try_from(pallet);
let call = PalletCallNameOf::<T>::try_from(call);
match (pallet, call) {
(Ok(pallet), Ok(call)) => Self::is_paused(&(pallet, call)),
_ => true,
}
}
/// Ensure that this call can be paused.
pub fn ensure_can_pause(full_name: &RuntimeCallNameOf<T>) -> Result<(), Error<T>> {
// SAFETY: The `TxPause` pallet can never pause itself.
if full_name.0.as_ref() == <Self as PalletInfoAccess>::name().as_bytes().to_vec() {
return Err(Error::<T>::Unpausable)
}
if T::WhitelistedCalls::contains(&full_name) {
return Err(Error::<T>::Unpausable)
}
if Self::is_paused(&full_name) {
return Err(Error::<T>::IsPaused)
}
Ok(())
}
/// Ensure that this call can be un-paused.
pub fn ensure_can_unpause(full_name: &RuntimeCallNameOf<T>) -> Result<(), Error<T>> {
if Self::is_paused(&full_name) {
// SAFETY: Everything that is paused, can be un-paused.
Ok(())
} else {
Err(Error::IsUnpaused)
}
}
}
impl<T: pallet::Config> Contains<<T as frame_system::Config>::RuntimeCall> for Pallet<T>
where
<T as frame_system::Config>::RuntimeCall: GetCallMetadata,
{
/// Return whether the call is allowed to be dispatched.
fn contains(call: &<T as frame_system::Config>::RuntimeCall) -> bool {
let CallMetadata { pallet_name, function_name } = call.get_call_metadata();
!Pallet::<T>::is_paused_unbound(pallet_name.into(), function_name.into())
}
}
impl<T: Config> frame_support::traits::TransactionPause for Pallet<T> {
type CallIdentifier = RuntimeCallNameOf<T>;
fn is_paused(full_name: Self::CallIdentifier) -> bool {
Self::is_paused(&full_name)
}
fn can_pause(full_name: Self::CallIdentifier) -> bool {
Self::ensure_can_pause(&full_name).is_ok()
}
fn pause(
full_name: Self::CallIdentifier,
) -> Result<(), frame_support::traits::TransactionPauseError> {
Self::do_pause(full_name).map_err(Into::into)
}
fn unpause(
full_name: Self::CallIdentifier,
) -> Result<(), frame_support::traits::TransactionPauseError> {
Self::do_unpause(full_name).map_err(Into::into)
}
}
impl<T: Config> From<Error<T>> for frame_support::traits::TransactionPauseError {
fn from(err: Error<T>) -> Self {
match err {
Error::<T>::NotFound => Self::NotFound,
Error::<T>::Unpausable => Self::Unpausable,
Error::<T>::IsPaused => Self::AlreadyPaused,
Error::<T>::IsUnpaused => Self::AlreadyUnpaused,
_ => Self::Unknown,
}
}
}