Skip to content

Commit

Permalink
Update tx-pause pallet to make it easier to pause stuff (#771)
Browse files Browse the repository at this point in the history
* Split UpdateOrigin to PauseOrigin and Upnause origin. Add list of unpausable pallets

Signed-off-by: Georgi Zlatarev <georgi.zlatarev@manta.network>

* Fix tests and add new ones

Signed-off-by: Georgi Zlatarev <georgi.zlatarev@manta.network>

* Add pause_pallets extrinsic to pause multiple pallets

Signed-off-by: Georgi Zlatarev <georgi.zlatarev@manta.network>

* Fix benchmark tests

Signed-off-by: Georgi Zlatarev <georgi.zlatarev@manta.network>

* Use 2/6 tech committee for PauseOrigin

Signed-off-by: Georgi Zlatarev <georgi.zlatarev@manta.network>

* update tx-pause tests

Signed-off-by: zqhxuyuan <zqhxuyuan@gmail.com>

* pause_pallets and weight draft

Signed-off-by: zqhxuyuan <zqhxuyuan@gmail.com>

* update weight calculation

Signed-off-by: zqhxuyuan <zqhxuyuan@gmail.com>

* weight test works

Signed-off-by: zqhxuyuan <zqhxuyuan@gmail.com>

* fix clippy

Signed-off-by: zqhxuyuan <zqhxuyuan@gmail.com>

* TxPause pallet whitelist in filter

Signed-off-by: zqhxuyuan <zqhxuyuan@gmail.com>

* change MaxCallNames to 25

Signed-off-by: zqhxuyuan <zqhxuyuan@gmail.com>

* rename to NonPausablePallets

Signed-off-by: zqhxuyuan <zqhxuyuan@gmail.com>

* EnsureMembers not work on manta yet

Signed-off-by: zqhxuyuan <zqhxuyuan@gmail.com>

* fix syntax change

Signed-off-by: Adam Reif <Garandor@manta.network>

Signed-off-by: Georgi Zlatarev <georgi.zlatarev@manta.network>
Signed-off-by: zqhxuyuan <zqhxuyuan@gmail.com>
Signed-off-by: Adam Reif <Garandor@manta.network>
Co-authored-by: zqhxuyuan <zqhxuyuan@gmail.com>
Co-authored-by: Adam Reif <garandor@manta.network>
Signed-off-by: Charles Ferrell <charlie@manta.network>
  • Loading branch information
3 people authored and ferrell-code committed Dec 27, 2022
1 parent 56746fe commit d55dad6
Show file tree
Hide file tree
Showing 8 changed files with 675 additions and 89 deletions.
8 changes: 4 additions & 4 deletions pallets/tx-pause/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ pub fn assert_last_event<T: Config>(generic_event: <T as Config>::Event) {
benchmarks! {
// Benchmark `pause_transaction` extrinsic:
pause_transaction {
let pallet_name = b"Balances".to_vec();
let function_name = b"transfer".to_vec();
let pallet_name = b"System".to_vec();
let function_name = b"remark".to_vec();
}: pause_transaction(RawOrigin::Root, pallet_name.clone(), function_name.clone())
verify {
assert_last_event::<T>(
Expand All @@ -48,8 +48,8 @@ benchmarks! {
// Benchmark `unpause_transaction` extrinsic:
unpause_transaction {
let origin: T::Origin = T::Origin::from(RawOrigin::Root);
let pallet_name = b"Balances".to_vec();
let function_name = b"transfer".to_vec();
let pallet_name = b"System".to_vec();
let function_name = b"remark".to_vec();
TransactionPause::<T>::pause_transaction(origin, pallet_name.clone(), function_name.clone())?;
}: unpause_transaction(RawOrigin::Root, pallet_name.clone(), function_name.clone())
verify {
Expand Down
242 changes: 213 additions & 29 deletions pallets/tx-pause/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ pub mod weights;
pub use pallet::*;
pub use weights::WeightInfo;

type CallOf<T> = <T as Config>::Call;

#[frame_support::pallet]
pub mod pallet {
use super::*;
Expand All @@ -51,8 +53,18 @@ pub mod pallet {
pub trait Config: frame_system::Config {
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;

/// The origin which may set filter.
type UpdateOrigin: EnsureOrigin<Self::Origin>;
type Call: Parameter + GetCallMetadata;

type MaxCallNames: Get<u32>;

/// The origin which may add to filter.
type PauseOrigin: EnsureOrigin<Self::Origin>;

/// The origin which may remove from filter.
type UnpauseOrigin: EnsureOrigin<Self::Origin>;

/// Names of pallets which cannot be paused.
type NonPausablePallets: Contains<Vec<u8>>;

/// Weight information for the extrinsics in this pallet.
type WeightInfo: WeightInfo;
Expand All @@ -64,15 +76,21 @@ pub mod pallet {
CannotPause,
/// invalid character encoding
InvalidCharacter,
/// call of pallet too many
TooManyCalls,
}

#[pallet::event]
#[pallet::generate_deposit(fn deposit_event)]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// Paused transaction . \[pallet_name_bytes, function_name_bytes\]
TransactionPaused(Vec<u8>, Vec<u8>),
/// Unpaused transaction . \[pallet_name_bytes, function_name_bytes\]
TransactionUnpaused(Vec<u8>, Vec<u8>),
/// Paused pallet
PalletPaused(Vec<u8>),
/// Unpaused pallet
PalletUnpaused(Vec<u8>),
}

/// The paused transaction map
Expand Down Expand Up @@ -103,25 +121,12 @@ pub mod pallet {
pallet_name: Vec<u8>,
function_name: Vec<u8>,
) -> DispatchResult {
T::UpdateOrigin::ensure_origin(origin)?;

// not allowed to pause calls of this pallet to ensure safe
let pallet_name_string =
sp_std::str::from_utf8(&pallet_name).map_err(|_| Error::<T>::InvalidCharacter)?;
ensure!(
pallet_name_string != <Self as PalletInfoAccess>::name(),
Error::<T>::CannotPause
);

PausedTransactions::<T>::mutate_exists(
(pallet_name.clone(), function_name.clone()),
|maybe_paused| {
if maybe_paused.is_none() {
*maybe_paused = Some(());
Self::deposit_event(Event::TransactionPaused(pallet_name, function_name));
}
},
);
T::PauseOrigin::ensure_origin(origin)?;

Self::ensure_can_pause(&pallet_name)?;

Self::pause_one(&pallet_name, &function_name, true)?;

Ok(())
}

Expand All @@ -135,21 +140,200 @@ pub mod pallet {
pallet_name: Vec<u8>,
function_name: Vec<u8>,
) -> DispatchResult {
T::UpdateOrigin::ensure_origin(origin)?;
if PausedTransactions::<T>::take((&pallet_name, &function_name)).is_some() {
Self::deposit_event(Event::TransactionUnpaused(pallet_name, function_name));
};
T::UnpauseOrigin::ensure_origin(origin)?;

Self::unpause_one(&pallet_name, &function_name)?;

Ok(())
}

/// Pause extrinsics by passing the extrinsic and corresponding pallet names.
/// Use names as they are written in the source code of the pallet.
#[pallet::call_index(2)]
#[pallet::weight({
let len = pallet_and_funcs.iter().flat_map(|item| {item.clone().1}).count();
T::WeightInfo::pause_transaction().saturating_mul(len as Weight)
})]
#[transactional]
pub fn pause_transactions(
origin: OriginFor<T>,
pallet_and_funcs: Vec<(Vec<u8>, Vec<Vec<u8>>)>,
) -> DispatchResult {
T::PauseOrigin::ensure_origin(origin)?;

for (pallet_name, function_name) in pallet_and_funcs {
Self::ensure_can_pause(&pallet_name)?;

for call_name in function_name {
Self::pause_one(&pallet_name, &call_name, true)?;
}
}

Ok(())
}

/// Unpause extrinsics by passing the extrinsic and corresponding pallet names.
/// Use names as they are written in the source code of the pallet.
#[pallet::call_index(3)]
#[pallet::weight({
let len = pallet_and_funcs.iter().flat_map(|item| {item.clone().1}).count();
T::WeightInfo::unpause_transaction().saturating_mul(len as Weight)
})]
#[transactional]
pub fn unpause_transactions(
origin: OriginFor<T>,
pallet_and_funcs: Vec<(Vec<u8>, Vec<Vec<u8>>)>,
) -> DispatchResult {
T::UnpauseOrigin::ensure_origin(origin)?;

for (pallet_name, function_name) in pallet_and_funcs {
for call_name in function_name {
Self::unpause_one(&pallet_name, &call_name)?;
}
}

Ok(())
}

/// Pause all the calls of the listed pallets in `pallet_names`.
/// This logic is in its own extrinsic in order to not have to pause calls 1 by 1.
#[pallet::call_index(4)]
#[pallet::weight({
let len = pallet_names.len();
let max = T::MaxCallNames::get();
T::WeightInfo::pause_transaction().saturating_mul(len as Weight).saturating_mul(max as Weight)
})]
#[transactional]
pub fn pause_pallets(
origin: OriginFor<T>,
pallet_names: Vec<Vec<u8>>,
) -> DispatchResultWithPostInfo {
T::PauseOrigin::ensure_origin(origin)?;
let mut sum = 0;

for pallet_name in pallet_names {
Self::ensure_can_pause(&pallet_name)?;

let pallet_name_string = sp_std::str::from_utf8(&pallet_name)
.map_err(|_| Error::<T>::InvalidCharacter)?;

let function_name =
<CallOf<T> as GetCallMetadata>::get_call_names(pallet_name_string);
ensure!(
function_name.len() < T::MaxCallNames::get() as usize,
Error::<T>::TooManyCalls
);

for call_name in function_name {
let call_name = call_name.as_bytes().to_vec();

Self::pause_one(&pallet_name, &call_name, false)?;

sum += 1;
}

// deposit event for each pallet
Self::deposit_event(Event::PalletPaused(pallet_name));
}

Ok(Some(T::WeightInfo::pause_transaction().saturating_mul(sum as Weight)).into())
}

/// Unpause all the calls of the listed pallets in `pallet_names`.
/// This logic is in its own extrinsic in order to not have to pause calls 1 by 1.
#[pallet::call_index(5)]
#[pallet::weight({
let len = pallet_names.len();
let max = T::MaxCallNames::get();
T::WeightInfo::pause_transaction().saturating_mul(len as Weight).saturating_mul(max as Weight)
})]
#[transactional]
pub fn unpause_pallets(
origin: OriginFor<T>,
pallet_names: Vec<Vec<u8>>,
) -> DispatchResultWithPostInfo {
T::UnpauseOrigin::ensure_origin(origin)?;
let mut sum = 0;

for pallet_name in pallet_names {
let pallet_name_string = sp_std::str::from_utf8(&pallet_name)
.map_err(|_| Error::<T>::InvalidCharacter)?;

let function_name =
<CallOf<T> as GetCallMetadata>::get_call_names(pallet_name_string);
for call_name in function_name {
let call_name = call_name.as_bytes().to_vec();

PausedTransactions::<T>::take((&pallet_name, call_name));

sum += 1;
}

// deposit event for each pallet
Self::deposit_event(Event::PalletUnpaused(pallet_name));
}

Ok(Some(T::WeightInfo::pause_transaction().saturating_mul(sum as Weight)).into())
}
}
}

impl<T: Config> Pallet<T> {
fn ensure_can_pause(pallet_name: &Vec<u8>) -> DispatchResult {
let pallet_name_string =
sp_std::str::from_utf8(pallet_name).map_err(|_| Error::<T>::InvalidCharacter)?;

// not allowed to pause calls of this pallet to ensure safe
ensure!(
pallet_name_string != <Self as PalletInfoAccess>::name(),
Error::<T>::CannotPause
);

// not allowed to pause `NonPausablePallets`
ensure!(
!T::NonPausablePallets::contains(pallet_name),
Error::<T>::CannotPause
);

Ok(())
}

fn pause_one(
pallet_name: &Vec<u8>,
function_name: &Vec<u8>,
deposit_event: bool,
) -> DispatchResult {
PausedTransactions::<T>::mutate_exists((pallet_name, function_name), |maybe_paused| {
if maybe_paused.is_none() {
*maybe_paused = Some(());
if deposit_event {
Self::deposit_event(Event::TransactionPaused(
pallet_name.clone(),
function_name.clone(),
));
}
}
});
Ok(())
}

fn unpause_one(pallet_name: &Vec<u8>, function_name: &Vec<u8>) -> DispatchResult {
if PausedTransactions::<T>::take((pallet_name, function_name)).is_some() {
Self::deposit_event(Event::TransactionUnpaused(
pallet_name.clone(),
function_name.clone(),
));
};
Ok(())
}
}

pub struct PausedTransactionFilter<T>(sp_std::marker::PhantomData<T>);
impl<T: Config> Contains<T::Call> for PausedTransactionFilter<T>
impl<T: Config> Contains<CallOf<T>> for PausedTransactionFilter<T>
where
<T as frame_system::Config>::Call: GetCallMetadata,
CallOf<T>: GetCallMetadata,
{
fn contains(call: &T::Call) -> bool {
fn contains(call: &CallOf<T>) -> bool {
let CallMetadata {
function_name,
pallet_name,
Expand Down
20 changes: 15 additions & 5 deletions pallets/tx-pause/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,27 @@
#![cfg(test)]

use super::*;
use frame_support::{construct_runtime, ord_parameter_types, parameter_types, traits::ConstU32};
use frame_support::{
construct_runtime, ord_parameter_types, parameter_types,
traits::{ConstU32, IsInVec},
};
use frame_system::EnsureRoot;
use manta_primitives::types::{Balance, BlockNumber, Header};

use sp_core::H256;
use sp_runtime::traits::{BlakeTwo256, IdentityLookup};

pub type AccountId = u128;
pub const ALICE: AccountId = 1;

mod tx_pause {
pub use super::super::*;
}

// Don't allow permission-less asset creation.
pub struct BaseFilter;
impl Contains<Call> for BaseFilter {
fn contains(call: &Call) -> bool {
!tx_pause::PausedTransactionFilter::<Runtime>::contains(call) // filter paused calls
// filter paused calls
!tx_pause::PausedTransactionFilter::<Runtime>::contains(call)
}
}

Expand Down Expand Up @@ -91,9 +93,17 @@ ord_parameter_types! {
pub const One: AccountId = 1;
}

parameter_types! {
pub NonPausablePallets: Vec<Vec<u8>> = vec![b"Democracy".to_vec(), b"Balances".to_vec(), b"Council".to_vec(), b"CouncilCollective".to_vec(), b"TechnicalCommittee".to_vec(), b"TechnicalCollective".to_vec()];
}

impl Config for Runtime {
type Event = Event;
type UpdateOrigin = EnsureRoot<AccountId>;
type Call = Call;
type MaxCallNames = ConstU32<10>;
type PauseOrigin = EnsureRoot<AccountId>;
type UnpauseOrigin = EnsureRoot<AccountId>;
type NonPausablePallets = IsInVec<NonPausablePallets>;
type WeightInfo = ();
}

Expand Down
Loading

0 comments on commit d55dad6

Please sign in to comment.