Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pause transaction #1333

Merged
merged 10 commits into from
Aug 18, 2021
22 changes: 22 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 39 additions & 0 deletions modules/transaction-pause/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
[package]
name = "module-transaction-pause"
version = "1.3.0"
authors = ["Acala Developers"]
edition = "2018"

[dependencies]
serde = { version = "1.0.124", optional = true }
codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.8", default-features = false }
frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.8", default-features = false }
frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.8", default-features = false }
sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.8", default-features = false }
sp-io= { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.8", default-features = false }
primitives = { package = "acala-primitives", path = "../../primitives", default-features = false }
support = { package = "module-support", path = "../support", default-features = false }
orml-traits = { path = "../../orml/traits", default-features = false }

[dev-dependencies]
sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.8" }
pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.8" }
orml-tokens = { path = "../../orml/tokens" }
smallvec = "1.4.1"

[features]
default = ["std"]
std = [
"serde",
"codec/std",
"sp-runtime/std",
"frame-support/std",
"frame-system/std",
"sp-io/std",
"sp-std/std",
"primitives/std",
"support/std",
"orml-traits/std",
]
try-runtime = ["frame-support/try-runtime"]
135 changes: 135 additions & 0 deletions modules/transaction-pause/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// This file is part of Acala.

// Copyright (C) 2020-2021 Acala Foundation.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

#![cfg_attr(not(feature = "std"), no_std)]
#![allow(clippy::unused_unit)]

use frame_support::{
dispatch::{CallMetadata, GetCallMetadata},
pallet_prelude::*,
traits::{Filter, PalletInfoAccess},
transactional,
};
use frame_system::pallet_prelude::*;
use sp_runtime::DispatchResult;
use sp_std::{prelude::*, vec::Vec};

mod mock;
mod tests;
pub mod weights;

pub use module::*;
pub use weights::WeightInfo;

#[frame_support::pallet]
pub mod module {
use super::*;

#[pallet::config]
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>;

/// Weight information for the extrinsics in this module.
type WeightInfo: WeightInfo;
}

#[pallet::error]
pub enum Error<T> {
/// can not pause
CannotPause,
/// invalid character encoding
InvalidCharacter,
}

#[pallet::event]
#[pallet::generate_deposit(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>),
}

/// The paused transaction map
///
/// map (PalletNameBytes, FunctionNameBytes) => Option<()>
#[pallet::storage]
#[pallet::getter(fn paused_transactions)]
pub type PausedTransactions<T: Config> = StorageMap<_, Twox64Concat, (Vec<u8>, Vec<u8>), (), OptionQuery>;

#[pallet::pallet]
pub struct Pallet<T>(_);

#[pallet::hooks]
impl<T: Config> Hooks<T::BlockNumber> for Pallet<T> {}

#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::weight(T::WeightInfo::pause_transaction())]
#[transactional]
pub fn pause_transaction(origin: OriginFor<T>, 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));
}
});
Ok(())
}

#[pallet::weight(T::WeightInfo::unpause_transaction())]
#[transactional]
pub fn unpause_transaction(
origin: OriginFor<T>,
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));
};
Ok(())
}
}
}

pub struct NonPausedTransactionFilter<T>(sp_std::marker::PhantomData<T>);
impl<T: Config> Filter<T::Call> for NonPausedTransactionFilter<T>
where
<T as frame_system::Config>::Call: GetCallMetadata,
{
fn filter(call: &T::Call) -> bool {
let CallMetadata {
function_name,
pallet_name,
} = call.get_call_metadata();
PausedTransactions::<T>::get((pallet_name.as_bytes(), function_name.as_bytes())).is_none()
}
}
146 changes: 146 additions & 0 deletions modules/transaction-pause/src/mock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
// This file is part of Acala.

// Copyright (C) 2020-2021 Acala Foundation.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

//! Mocks for the transaction pause module.

#![cfg(test)]

use super::*;
use frame_support::{construct_runtime, ord_parameter_types, parameter_types};
use frame_system::EnsureSignedBy;
use orml_traits::parameter_type_with_key;
use primitives::{Amount, Balance, CurrencyId, TokenSymbol};
use sp_core::H256;
use sp_runtime::{testing::Header, traits::IdentityLookup};

pub type AccountId = u128;
pub const ALICE: AccountId = 1;
pub const AUSD: CurrencyId = CurrencyId::Token(TokenSymbol::AUSD);

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

parameter_types! {
pub const BlockHashCount: u64 = 250;
}

impl frame_system::Config for Runtime {
type Origin = Origin;
type Index = u64;
type BlockNumber = u64;
type Call = Call;
type Hash = H256;
type Hashing = ::sp_runtime::traits::BlakeTwo256;
type AccountId = AccountId;
type Lookup = IdentityLookup<AccountId>;
type Header = Header;
type Event = Event;
type BlockHashCount = BlockHashCount;
type BlockWeights = ();
type BlockLength = ();
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = pallet_balances::AccountData<Balance>;
type OnNewAccount = ();
type OnKilledAccount = ();
type DbWeight = ();
type BaseCallFilter = ();
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
}

parameter_types! {
pub const NativeTokenExistentialDeposit: Balance = 10;
pub const MaxReserves: u32 = 50;
}

impl pallet_balances::Config for Runtime {
type Balance = Balance;
type DustRemoval = ();
type Event = Event;
type ExistentialDeposit = NativeTokenExistentialDeposit;
type AccountStore = System;
type MaxLocks = ();
type MaxReserves = MaxReserves;
type ReserveIdentifier = ();
type WeightInfo = ();
}

parameter_type_with_key! {
pub ExistentialDeposits: |_currency_id: CurrencyId| -> Balance {
Default::default()
};
}

impl orml_tokens::Config for Runtime {
type Event = Event;
type Balance = Balance;
type Amount = Amount;
type CurrencyId = CurrencyId;
type WeightInfo = ();
type ExistentialDeposits = ExistentialDeposits;
type OnDust = ();
type MaxLocks = ();
type DustRemovalWhitelist = ();
}

ord_parameter_types! {
pub const One: AccountId = 1;
}

impl Config for Runtime {
type Event = Event;
type UpdateOrigin = EnsureSignedBy<One, AccountId>;
type WeightInfo = ();
}

type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Runtime>;
type Block = frame_system::mocking::MockBlock<Runtime>;

construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
TransactionPause: transaction_pause::{Pallet, Storage, Call, Event<T>},
Balances: pallet_balances::{Pallet, Storage, Call, Event<T>},
Tokens: orml_tokens::{Pallet, Storage, Call, Event<T>},
}
);

pub struct ExtBuilder;

impl Default for ExtBuilder {
fn default() -> Self {
ExtBuilder
}
}

impl ExtBuilder {
pub fn build(self) -> sp_io::TestExternalities {
let t = frame_system::GenesisConfig::default()
.build_storage::<Runtime>()
.unwrap();

t.into()
}
}
Loading