From 1c06e621dc89c85b4ae7c7e0d7f2c93f80d80f54 Mon Sep 17 00:00:00 2001 From: Valentin Fernandez Date: Tue, 23 May 2023 15:06:21 -0300 Subject: [PATCH 1/2] lockdown-pallet backport --- Cargo.lock | 50 ++++++++ node/service/src/chain_spec/trappist.rs | 2 + pallets/lockdown-mode/Cargo.toml | 77 +++++++++++ pallets/lockdown-mode/README.md | 41 ++++++ pallets/lockdown-mode/src/benchmarking.rs | 25 ++++ pallets/lockdown-mode/src/lib.rs | 148 ++++++++++++++++++++++ pallets/lockdown-mode/src/mock.rs | 126 ++++++++++++++++++ pallets/lockdown-mode/src/tests.rs | 71 +++++++++++ pallets/lockdown-mode/src/weights.rs | 71 +++++++++++ primitives/xcm/Cargo.toml | 2 + primitives/xcm/src/lib.rs | 17 +++ runtime/trappist/Cargo.toml | 6 +- runtime/trappist/src/impls.rs | 47 ++++++- runtime/trappist/src/lib.rs | 26 +++- xcm-playground.toml | 5 + 15 files changed, 704 insertions(+), 10 deletions(-) create mode 100644 pallets/lockdown-mode/Cargo.toml create mode 100644 pallets/lockdown-mode/README.md create mode 100644 pallets/lockdown-mode/src/benchmarking.rs create mode 100644 pallets/lockdown-mode/src/lib.rs create mode 100644 pallets/lockdown-mode/src/mock.rs create mode 100644 pallets/lockdown-mode/src/tests.rs create mode 100644 pallets/lockdown-mode/src/weights.rs diff --git a/Cargo.lock b/Cargo.lock index da99bd55..2ea8ec84 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6043,6 +6043,39 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-lockdown-mode" +version = "0.1.0" +dependencies = [ + "cumulus-pallet-dmp-queue", + "cumulus-pallet-xcmp-queue", + "cumulus-primitives-core", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-assets", + "pallet-balances", + "pallet-remark", + "pallet-xcm", + "parachain-info", + "parachains-common", + "parity-scale-codec", + "polkadot-core-primitives", + "polkadot-parachain", + "polkadot-runtime-parachains", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "xcm", + "xcm-builder", + "xcm-executor", + "xcm-primitives", + "xcm-simulator", +] + [[package]] name = "pallet-membership" version = "4.0.0-dev" @@ -6282,6 +6315,21 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-remark" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-scheduler" version = "4.0.0-dev" @@ -12378,6 +12426,7 @@ dependencies = [ "pallet-dex-rpc-runtime-api", "pallet-identity", "pallet-insecure-randomness-collective-flip", + "pallet-lockdown-mode", "pallet-multisig", "pallet-preimage", "pallet-scheduler", @@ -14005,6 +14054,7 @@ name = "xcm-primitives" version = "0.0.1" dependencies = [ "frame-support", + "sp-runtime", "sp-std", "xcm", "xcm-executor", diff --git a/node/service/src/chain_spec/trappist.rs b/node/service/src/chain_spec/trappist.rs index b2053db4..5d9fa82c 100644 --- a/node/service/src/chain_spec/trappist.rs +++ b/node/service/src/chain_spec/trappist.rs @@ -233,6 +233,7 @@ fn testnet_genesis( phantom: Default::default(), }, treasury: Default::default(), + lockdown_mode: Default::default(), } } @@ -349,5 +350,6 @@ fn trappist_live_genesis( phantom: Default::default(), }, treasury: Default::default(), + lockdown_mode: Default::default(), } } diff --git a/pallets/lockdown-mode/Cargo.toml b/pallets/lockdown-mode/Cargo.toml new file mode 100644 index 00000000..14746944 --- /dev/null +++ b/pallets/lockdown-mode/Cargo.toml @@ -0,0 +1,77 @@ +[package] +name = "pallet-lockdown-mode" +version = "0.1.0" +description = "Trappist pallet for setting lockdown mode." +edition = "2021" +license = "Apache-2.0" +repository = "https://github.com/paritytech/trappist" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive",] } +scale-info = { version = "2.3.1", default-features = false, features = ["derive"] } +sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.40" } +sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.40" } +frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } +frame-support = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } +frame-system = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } +cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.40", default-features = false } +pallet-assets = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } +pallet-balances = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } +log = "0.4.17" +xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v0.9.40" } + +xcm-primitives = { path = "../../primitives/xcm", default-features = false } + +[dev-dependencies] +sp-core = { version = "7.0.0", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } +sp-io = { version = "7.0.0", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } +sp-runtime = { version = "7.0.0", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } +pallet-remark = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } + + +xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v0.9.40" } +xcm-simulator = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v0.9.40" } +xcm-executor = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v0.9.40" } +xcm-builder = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v0.9.40" } +pallet-xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v0.9.40" } +polkadot-core-primitives = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v0.9.40" } +polkadot-runtime-parachains = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v0.9.40" } +polkadot-parachain = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v0.9.40" } + +parachain-info = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.40", default-features = false } +parachains-common = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.40", default-features = false } +cumulus-pallet-dmp-queue = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.40", default-features = false } +cumulus-pallet-xcmp-queue = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.40", default-features = false } +cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.40", default-features = false } + +[features] +default = ["std"] +std = [ + "codec/std", + "sp-runtime/std", + "sp-std/std", + "pallet-assets/std", + "pallet-balances/std", + "frame-benchmarking/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "xcm-primitives/std", + "xcm/std", + "xcm-executor/std", + "xcm-builder/std", + "pallet-xcm/std", + "polkadot-core-primitives/std", + "polkadot-runtime-parachains/std", + "polkadot-parachain/std", + "parachain-info/std", + "parachains-common/std", + "cumulus-pallet-dmp-queue/std", + "cumulus-pallet-xcmp-queue/std", + "cumulus-primitives-core/std", +] +runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] +try-runtime = ["frame-support/try-runtime"] \ No newline at end of file diff --git a/pallets/lockdown-mode/README.md b/pallets/lockdown-mode/README.md new file mode 100644 index 00000000..94e9686c --- /dev/null +++ b/pallets/lockdown-mode/README.md @@ -0,0 +1,41 @@ +# Lockdown Mode Pallet + +The Lockdown Mode Pallet is a Substrate module that provides functionality to lock down the runtime execution in a Substrate-based blockchain system. When the lockdown mode is activated, it filters out incoming calls and messages to ensure that only authorized actions are allowed. + +## Overview + +This pallet the governance of the chain to activate or deactivate a lockdown mode. When the lockdown mode is activated, incoming runtime calls and downward messages are filtered based on a preconfigured filter. Additionally, it suspends the execution of XCM (Cross-Consensus Message) messages in the `on_idle` hook. + +The lockdown mode status is stored in the `LockdownModeStatus` storage item. When the lockdown mode is deactivated, the system resumes normal operations, including the execution of XCM messages in the `on_idle` hook. + +## Configuration + +This pallet supports configurable traits that allow customization according to specific needs. + +### Types + +- `RuntimeEvent`: Specifies the runtime event type. +- `LockdownModeOrigin`: Specifies the origin that is allowed to activate and deactivate the lockdown mode. +- `BlackListedCalls`: Specifies the filter used to filter incoming runtime calls in lockdown mode. +- `LockdownDmpHandler`: Specifies the handler for downward messages in lockdown mode. +- `XcmExecutorManager`: Interface to control the execution of XCMP Queue messages. + + +## Extrinsics + +The pallet provides the following extrinsics: + +- `activate_lockdown_mode`: Activates the lockdown mode. Only the specified `LockdownModeOrigin` can call this extrinsic. It updates the `LockdownModeStatus` storage item to `ACTIVATED` (true) and attempts to suspend the execution of XCM messages in the `on_idle` hook. +- `deactivate_lockdown_mode`: Deactivates the lockdown mode. Only the specified `LockdownModeOrigin` can call this extrinsic. It updates the `LockdownModeStatus` storage item to `DEACTIVATED` (false) and attempts to resume the execution of XCM messages in the `on_idle` hook. + + +#### Errors + +Possible errors returned by the dispatchable calls are: + +- `LockdownModeAlreadyActivated`: The lockdown mode is already activated. +- `LockdownModeAlreadyDeactivated`: The lockdown mode is already deactivated. + +Please note that any failure to suspend or resume XCM execution in the `on_idle` hook is not treated as a fatal error that stops the function execution. Instead, it is recorded as an event `FailedToSuspendIdleXcmExecution` or `FailedToResumeIdleXcmExecution`, respectively, and the function continues its execution. + +The lockdown mode can serve as a crucial tool in system maintenance or in case of emergency, when it's necessary to restrict system operation and ensure the system's security and stability. \ No newline at end of file diff --git a/pallets/lockdown-mode/src/benchmarking.rs b/pallets/lockdown-mode/src/benchmarking.rs new file mode 100644 index 00000000..9f98ead7 --- /dev/null +++ b/pallets/lockdown-mode/src/benchmarking.rs @@ -0,0 +1,25 @@ +use super::*; + +#[allow(unused)] +use crate::Pallet as LockdownMode; +use crate::{ACTIVATED, DEACTIVATED}; +use frame_benchmarking::benchmarks; +use frame_system::RawOrigin; + +benchmarks! { + activate_lockdown_mode { + LockdownModeStatus::::put(DEACTIVATED); + }: activate_lockdown_mode(RawOrigin::Root) + verify { + assert_eq!(LockdownModeStatus::::get(), ACTIVATED); + } + + deactivate_lockdown_mode { + LockdownModeStatus::::put(ACTIVATED); + }: deactivate_lockdown_mode(RawOrigin::Root) + verify { + assert_eq!(LockdownModeStatus::::get(), DEACTIVATED); + } + + impl_benchmark_test_suite!(LockdownMode, crate::mock::new_test_ext(), crate::mock::Test); +} diff --git a/pallets/lockdown-mode/src/lib.rs b/pallets/lockdown-mode/src/lib.rs new file mode 100644 index 00000000..dd035e32 --- /dev/null +++ b/pallets/lockdown-mode/src/lib.rs @@ -0,0 +1,148 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +/// Edit this file to define custom logic or remove it if it is not needed. +/// Learn more about FRAME and the core library of Substrate FRAME pallets: +/// +pub use pallet::*; + +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; +pub mod weights; +pub use weights::*; + +pub const ACTIVATED: bool = true; +pub const DEACTIVATED: bool = false; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use cumulus_primitives_core::{ + relay_chain::BlockNumber as RelayBlockNumber, DmpMessageHandler, + }; + use frame_support::{ + pallet_prelude::{ValueQuery, *}, + traits::Contains, + }; + use frame_system::pallet_prelude::*; + use sp_std::vec::Vec; + use xcm_primitives::PauseXcmExecution; + #[pallet::pallet] + pub struct Pallet(_); + + #[derive(Default)] + #[pallet::genesis_config] + /// Genesis config for lockdown mode pallet + pub struct GenesisConfig {} + + #[pallet::genesis_build] + impl GenesisBuild for GenesisConfig { + fn build(&self) { + LockdownModeStatus::::put(ACTIVATED); + } + } + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + type LockdownModeOrigin: EnsureOrigin; + type BlackListedCalls: Contains; + type LockdownDmpHandler: DmpMessageHandler; + type XcmExecutorManager: PauseXcmExecution; + type WeightInfo: WeightInfo; + } + + #[pallet::storage] + pub type LockdownModeStatus = StorageValue<_, bool, ValueQuery>; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + LockdownModeActivated, + LockdownModeDeactivated, + /// The call to suspend on_idle XCM execution failed with inner error + FailedToSuspendIdleXcmExecution { + error: DispatchError, + }, + /// The call to resume on_idle XCM execution failed with inner error + FailedToResumeIdleXcmExecution { + error: DispatchError, + }, + } + + #[pallet::error] + pub enum Error { + /// Lockdown mode was already activated + LockdownModeAlreadyActivated, + /// Lockdown mode was already deactivated + LockdownModeAlreadyDeactivated, + } + + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + #[pallet::weight(::WeightInfo::activate_lockdown_mode())] + pub fn activate_lockdown_mode(origin: OriginFor) -> DispatchResult { + T::LockdownModeOrigin::ensure_origin(origin)?; + + ensure!(!LockdownModeStatus::::get(), Error::::LockdownModeAlreadyActivated); + + LockdownModeStatus::::put(ACTIVATED); + + if let Err(error) = T::XcmExecutorManager::suspend_xcm_execution() { + log::error!("Failed to suspend idle XCM execution {:?}", error); + Self::deposit_event(Event::FailedToSuspendIdleXcmExecution { error }); + } + + Self::deposit_event(Event::LockdownModeActivated); + + Ok(()) + } + + #[pallet::call_index(1)] + #[pallet::weight(::WeightInfo::deactivate_lockdown_mode())] + pub fn deactivate_lockdown_mode(origin: OriginFor) -> DispatchResult { + T::LockdownModeOrigin::ensure_origin(origin)?; + ensure!(LockdownModeStatus::::get(), Error::::LockdownModeAlreadyDeactivated); + + LockdownModeStatus::::put(DEACTIVATED); + + if let Err(error) = T::XcmExecutorManager::resume_xcm_execution() { + log::error!("Failed to resume idle XCM execution {:?}", error); + Self::deposit_event(Event::FailedToResumeIdleXcmExecution { error }); + } + + Self::deposit_event(Event::LockdownModeDeactivated); + + Ok(()) + } + } + + impl Contains for Pallet { + fn contains(call: &T::RuntimeCall) -> bool { + if LockdownModeStatus::::get() { + T::BlackListedCalls::contains(call) + } else { + return true + } + } + } + + impl DmpMessageHandler for Pallet { + fn handle_dmp_messages( + iter: impl Iterator)>, + limit: Weight, + ) -> Weight { + if LockdownModeStatus::::get() { + T::LockdownDmpHandler::handle_dmp_messages(iter, Weight::zero()) + } else { + // Normal path, everything should pass through + T::LockdownDmpHandler::handle_dmp_messages(iter, limit) + } + } + } +} diff --git a/pallets/lockdown-mode/src/mock.rs b/pallets/lockdown-mode/src/mock.rs new file mode 100644 index 00000000..e685a875 --- /dev/null +++ b/pallets/lockdown-mode/src/mock.rs @@ -0,0 +1,126 @@ +use crate as pallet_lockdown_mode; +use cumulus_primitives_core::{relay_chain::BlockNumber as RelayBlockNumber, DmpMessageHandler}; +use frame_support::{ + traits::{ConstU16, ConstU64, Contains}, + weights::Weight, +}; +use frame_system as system; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, ConstU32, IdentityLookup}, + DispatchResult, +}; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +frame_support::parameter_types! { + pub const StatemineParaIdInfo: u32 = 1000u32; + pub const StatemineAssetsInstanceInfo: u8 = 50u8; + pub const StatemineAssetIdInfo: u128 = 1u128; +} + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system, + LockdownMode: pallet_lockdown_mode::{Pallet, Call, Storage, Event}, + Balance: pallet_balances::{Pallet, Call, Storage, Event}, + Remark: pallet_remark::{Pallet, Call, Storage, Event}, + } +); + +impl system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = ConstU16<42>; + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +impl pallet_balances::Config for Test { + type Balance = u64; + type DustRemoval = (); + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposit = ConstU64<1>; + type AccountStore = System; + type WeightInfo = (); + type MaxLocks = (); + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; +} + +pub struct RuntimeBlackListedCalls; +impl Contains for RuntimeBlackListedCalls { + fn contains(call: &RuntimeCall) -> bool { + match call { + RuntimeCall::Balance(_) => false, + _ => true, + } + } +} + +pub struct LockdownDmpHandler; +impl DmpMessageHandler for LockdownDmpHandler { + fn handle_dmp_messages( + _iter: impl Iterator)>, + limit: Weight, + ) -> Weight { + limit + } +} + +pub struct XcmExecutionManager {} + +impl xcm_primitives::PauseXcmExecution for XcmExecutionManager { + fn suspend_xcm_execution() -> DispatchResult { + Ok(()) + } + fn resume_xcm_execution() -> DispatchResult { + Ok(()) + } +} + +impl pallet_remark::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); +} + +impl pallet_lockdown_mode::Config for Test { + type RuntimeEvent = RuntimeEvent; + type LockdownModeOrigin = frame_system::EnsureRoot; + type BlackListedCalls = RuntimeBlackListedCalls; + type LockdownDmpHandler = LockdownDmpHandler; + type XcmExecutorManager = XcmExecutionManager; + type WeightInfo = pallet_lockdown_mode::weights::SubstrateWeight; +} + +// Build genesis storage according to the mock runtime. +pub fn new_test_ext() -> sp_io::TestExternalities { + let storage = system::GenesisConfig::default().build_storage::().unwrap(); + storage.into() +} diff --git a/pallets/lockdown-mode/src/tests.rs b/pallets/lockdown-mode/src/tests.rs new file mode 100644 index 00000000..cf466b52 --- /dev/null +++ b/pallets/lockdown-mode/src/tests.rs @@ -0,0 +1,71 @@ +use super::*; +use crate::{mock::*, Error, ACTIVATED, DEACTIVATED}; +use frame_support::{assert_noop, assert_ok, traits::Contains}; +use pallet_balances::{self, Call as BalancesCall}; +use pallet_remark::{self, Call as RemarkCall}; + +#[test] +fn activate_lockdown_mode_works() { + new_test_ext().execute_with(|| { + assert_eq!(LockdownModeStatus::::get(), DEACTIVATED); + assert_ok!(LockdownMode::activate_lockdown_mode(RuntimeOrigin::root())); + + let lockdown_mode = LockdownModeStatus::::get(); + assert_eq!(lockdown_mode, ACTIVATED); + + assert_noop!( + LockdownMode::activate_lockdown_mode(RuntimeOrigin::root(),), + Error::::LockdownModeAlreadyActivated + ); + }); +} + +#[test] +fn deactivate_lockdown_mode_works() { + new_test_ext().execute_with(|| { + // We activate lockdown mode first so we can deactivate it. + assert_ok!(LockdownMode::activate_lockdown_mode(RuntimeOrigin::root())); + + assert_ok!(LockdownMode::deactivate_lockdown_mode(RuntimeOrigin::root())); + + let lockdown_mode = LockdownModeStatus::::get(); + assert_eq!(lockdown_mode, DEACTIVATED); + + assert_noop!( + LockdownMode::deactivate_lockdown_mode(RuntimeOrigin::root(),), + Error::::LockdownModeAlreadyDeactivated + ); + }); +} + +#[test] +fn call_not_filtered_in_lockdown_mode() { + new_test_ext().execute_with(|| { + assert_ok!(LockdownMode::activate_lockdown_mode(RuntimeOrigin::root())); + let remark_call = RuntimeCall::Remark(RemarkCall::store { remark: vec![1, 2, 3] }); + let result: bool = LockdownMode::contains(&remark_call); + assert!(result); + }); +} + +#[test] +fn call_filtered_in_lockdown_mode() { + new_test_ext().execute_with(|| { + assert_ok!(LockdownMode::activate_lockdown_mode(RuntimeOrigin::root())); + let balance_call = RuntimeCall::Balance(BalancesCall::transfer { dest: 1, value: 2 }); + + let result: bool = LockdownMode::contains(&balance_call); + assert!(!result); + }); +} + +#[test] +fn call_not_filtered_in_normal_mode() { + new_test_ext().execute_with(|| { + let lockdown_mode = LockdownModeStatus::::get(); + assert_eq!(lockdown_mode, DEACTIVATED); + let balance_call = RuntimeCall::Balance(BalancesCall::transfer { dest: 1, value: 2 }); + let result: bool = LockdownMode::contains(&balance_call); + assert!(result); + }); +} diff --git a/pallets/lockdown-mode/src/weights.rs b/pallets/lockdown-mode/src/weights.rs new file mode 100644 index 00000000..937b79d2 --- /dev/null +++ b/pallets/lockdown-mode/src/weights.rs @@ -0,0 +1,71 @@ +//! Autogenerated weights for `pallet_lockdown_mode` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-05-18, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `vale`, CPU: `11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/trappist-collator +// benchmark +// pallet +// --chain +// dev +// --pallet +// pallet_lockdown_mode +// --execution=wasm +// --wasm-execution=compiled +// --extrinsic +// * +// --steps +// 50 +// --repeat +// 20 +// --output +// pallets/lockdown-mode/src/weights.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +pub trait WeightInfo { + fn activate_lockdown_mode() -> Weight; + fn deactivate_lockdown_mode() -> Weight; +} + +/// Weight functions for `pallet_lockdown_mode`. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: LockdownMode LockdownModeStatus (r:1 w:1) + /// Proof: LockdownMode LockdownModeStatus (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: XcmpQueue QueueSuspended (r:0 w:1) + /// Proof Skipped: XcmpQueue QueueSuspended (max_values: Some(1), max_size: None, mode: Measured) + fn activate_lockdown_mode() -> Weight { + // Proof Size summary in bytes: + // Measured: `100` + // Estimated: `1586` + // Minimum execution time: 69_552_000 picoseconds. + Weight::from_parts(75_364_000, 0) + .saturating_add(Weight::from_parts(0, 1586)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: LockdownMode LockdownModeStatus (r:1 w:1) + /// Proof: LockdownMode LockdownModeStatus (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: XcmpQueue QueueSuspended (r:0 w:1) + /// Proof Skipped: XcmpQueue QueueSuspended (max_values: Some(1), max_size: None, mode: Measured) + fn deactivate_lockdown_mode() -> Weight { + // Proof Size summary in bytes: + // Measured: `100` + // Estimated: `1586` + // Minimum execution time: 42_162_000 picoseconds. + Weight::from_parts(43_321_000, 0) + .saturating_add(Weight::from_parts(0, 1586)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(2)) + } +} \ No newline at end of file diff --git a/primitives/xcm/Cargo.toml b/primitives/xcm/Cargo.toml index cd64530e..391b973a 100644 --- a/primitives/xcm/Cargo.toml +++ b/primitives/xcm/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.40" } +sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.40" } xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v0.9.40" } xcm-executor = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v0.9.40" } @@ -15,6 +16,7 @@ default = [ "std" ] std = [ "sp-std/std", "frame-support/std", + "sp-runtime/std", "xcm/std", "xcm-executor/std" ] diff --git a/primitives/xcm/src/lib.rs b/primitives/xcm/src/lib.rs index 45a9a0b1..aa482f6b 100644 --- a/primitives/xcm/src/lib.rs +++ b/primitives/xcm/src/lib.rs @@ -4,6 +4,7 @@ use frame_support::{ sp_runtime::SaturatedConversion, traits::{fungibles::Inspect, Currency}, }; +use sp_runtime::DispatchResult; use sp_std::{borrow::Borrow, marker::PhantomData, vec::Vec}; use xcm::{ latest::{ @@ -128,3 +129,19 @@ impl DispatchResult; + fn resume_xcm_execution() -> DispatchResult; +} +#[cfg(not(test))] +impl PauseXcmExecution for () { + fn suspend_xcm_execution() -> DispatchResult { + Ok(()) + } + fn resume_xcm_execution() -> DispatchResult { + Ok(()) + } +} diff --git a/runtime/trappist/Cargo.toml b/runtime/trappist/Cargo.toml index fb1a7ecf..9d78c90f 100644 --- a/runtime/trappist/Cargo.toml +++ b/runtime/trappist/Cargo.toml @@ -45,8 +45,6 @@ frame-system = { git = "https://github.com/paritytech/substrate", default-featur frame-system-benchmarking = { git = "https://github.com/paritytech/substrate", default-features = false, optional = true, branch = "polkadot-v0.9.40" } frame-system-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.40" } - - ## Substrate Pallet Dependencies pallet-assets = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.40" } pallet-asset-tx-payment = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.40" } @@ -71,7 +69,6 @@ pallet-uniques = { git = "https://github.com/paritytech/substrate", default-feat pallet-utility = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.40" } pallet-treasury = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.40" } - # Cumulus dependencies cumulus-pallet-aura-ext = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.40", default-features = false } cumulus-pallet-dmp-queue = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.40", default-features = false } @@ -105,6 +102,7 @@ pallet-chess = { git = "https://github.com/SubstrateChess/pallet-chess.git", def # Trappist Pallets pallet-asset-registry = { version = "0.0.1", default-features = false, path = "../../pallets/asset-registry" } +pallet-lockdown-mode = { version = "0.1.0", default-features = false, path = "../../pallets/lockdown-mode" } [features] default = ["std"] @@ -143,6 +141,7 @@ std = [ "pallet-dex/std", "pallet-dex-rpc-runtime-api/std", "pallet-identity/std", + "pallet-lockdown-mode/std", "pallet-multisig/std", "pallet-insecure-randomness-collective-flip/std", "pallet-scheduler/std", @@ -189,6 +188,7 @@ runtime-benchmarks = [ "pallet-democracy/runtime-benchmarks", "pallet-dex/runtime-benchmarks", "pallet-identity/runtime-benchmarks", + "pallet-lockdown-mode/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", "pallet-scheduler/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", diff --git a/runtime/trappist/src/impls.rs b/runtime/trappist/src/impls.rs index 13391c26..46ddb134 100644 --- a/runtime/trappist/src/impls.rs +++ b/runtime/trappist/src/impls.rs @@ -16,7 +16,14 @@ //! Auxiliary struct/enums for parachain runtimes. //! Taken from polkadot/runtime/common (at a21cd64) and adapted for parachains. -use frame_support::traits::{Currency, Imbalance, OnUnbalanced}; +use super::*; +use cumulus_primitives_core::{relay_chain::BlockNumber as RelayBlockNumber, DmpMessageHandler}; +use frame_support::{ + traits::{Contains, Currency, Imbalance, OnUnbalanced}, + weights::Weight, +}; +pub use log; +use sp_runtime::DispatchResult; use sp_std::marker::PhantomData; @@ -66,6 +73,44 @@ where } } } + +pub struct RuntimeBlackListedCalls; +impl Contains for RuntimeBlackListedCalls { + fn contains(call: &RuntimeCall) -> bool { + match call { + RuntimeCall::Balances(_) => false, + RuntimeCall::Assets(_) => false, + RuntimeCall::Dex(_) => false, + RuntimeCall::PolkadotXcm(_) => false, + RuntimeCall::Treasury(_) => false, + RuntimeCall::Chess(_) => false, + RuntimeCall::Contracts(_) => false, + RuntimeCall::Uniques(_) => false, + RuntimeCall::AssetRegistry(_) => false, + _ => true, + } + } +} + +pub struct LockdownDmpHandler; +impl DmpMessageHandler for LockdownDmpHandler { + fn handle_dmp_messages( + _iter: impl Iterator)>, + limit: Weight, + ) -> Weight { + DmpQueue::handle_dmp_messages(_iter, limit) + } +} + +pub struct XcmExecutionManager {} +impl xcm_primitives::PauseXcmExecution for XcmExecutionManager { + fn suspend_xcm_execution() -> DispatchResult { + XcmpQueue::suspend_xcm_execution(RuntimeOrigin::root()) + } + fn resume_xcm_execution() -> DispatchResult { + XcmpQueue::resume_xcm_execution(RuntimeOrigin::root()) + } +} #[cfg(test)] mod tests { use super::*; diff --git a/runtime/trappist/src/lib.rs b/runtime/trappist/src/lib.rs index c76bcbc6..fbb254c8 100644 --- a/runtime/trappist/src/lib.rs +++ b/runtime/trappist/src/lib.rs @@ -33,7 +33,7 @@ use sp_core::{crypto::KeyTypeId, ConstU8, OpaqueMetadata}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, ConvertInto}, - transaction_validity::{TransactionSource, TransactionValidity}, + transaction_validity::{InvalidTransaction, TransactionSource, TransactionValidity}, ApplyExtrinsicResult, Perbill, Percent, Permill, }; @@ -48,8 +48,8 @@ use frame_support::{ dispatch::DispatchClass, parameter_types, traits::{ - AsEnsureOriginWithArg, ConstU128, ConstU16, ConstU32, ConstU64, EitherOfDiverse, - EqualPrivilegeOnly, Everything, + AsEnsureOriginWithArg, ConstU128, ConstU16, ConstU32, ConstU64, Contains, EitherOfDiverse, + EqualPrivilegeOnly, }, weights::{ constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}, @@ -69,7 +69,7 @@ pub use parachains_common::{ NORMAL_DISPATCH_RATIO, SLOT_DURATION, }; -use impls::DealWithFees; +use impls::{DealWithFees, LockdownDmpHandler, RuntimeBlackListedCalls, XcmExecutionManager}; use xcm_config::{CollatorSelectionUpdateOrigin, RelayLocation}; @@ -170,7 +170,7 @@ parameter_types! { // Configure FRAME pallets to include in runtime. impl frame_system::Config for Runtime { - type BaseCallFilter = Everything; + type BaseCallFilter = LockdownMode; type BlockWeights = RuntimeBlockWeights; type BlockLength = RuntimeBlockLength; type AccountId = AccountId; @@ -281,7 +281,7 @@ impl cumulus_pallet_parachain_system::Config for Runtime { type RuntimeEvent = RuntimeEvent; type OnSystemEvent = (); type SelfParaId = parachain_info::Pallet; - type DmpMessageHandler = DmpQueue; + type DmpMessageHandler = LockdownMode; type ReservedDmpWeight = ReservedDmpWeight; type OutboundXcmpMessageSource = XcmpQueue; type XcmpMessageHandler = XcmpQueue; @@ -628,6 +628,15 @@ impl pallet_treasury::Config for Runtime { type SpendOrigin = frame_support::traits::NeverEnsureOrigin; } +impl pallet_lockdown_mode::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type LockdownModeOrigin = frame_system::EnsureRoot; + type BlackListedCalls = RuntimeBlackListedCalls; + type LockdownDmpHandler = LockdownDmpHandler; + type XcmExecutorManager = XcmExecutionManager; + type WeightInfo = pallet_lockdown_mode::weights::SubstrateWeight; +} + // Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( pub enum Runtime where @@ -669,6 +678,7 @@ construct_runtime!( Uniques: pallet_uniques = 43, Scheduler: pallet_scheduler = 44, Preimage: pallet_preimage = 45, + LockdownMode: pallet_lockdown_mode = 46, // Handy utilities. Utility: pallet_utility::{Pallet, Call, Event} = 50, @@ -707,6 +717,7 @@ mod benches { [pallet_contracts, Contracts] [pallet_collective, Council] [pallet_democracy, Democracy] + [pallet_lockdown_mode, LockdownMode] [pallet_treasury, Treasury] [pallet_assets, Assets] [pallet_dex, Dex] @@ -777,6 +788,9 @@ impl_runtime_apis! { tx: ::Extrinsic, block_hash: ::Hash, ) -> TransactionValidity { + if !::BaseCallFilter::contains(&tx.function) { + return InvalidTransaction::Call.into(); + }; Executive::validate_transaction(source, tx, block_hash) } } diff --git a/xcm-playground.toml b/xcm-playground.toml index 8da6cd97..d2f26c16 100644 --- a/xcm-playground.toml +++ b/xcm-playground.toml @@ -21,6 +21,11 @@ default_command = "./bin/polkadot" validator = true extra_args = [ "-lparachain=debug" ] + [[relaychain.nodes]] + name = "dave" + validator = true + extra_args = [ "-lparachain=debug" ] + [[parachains]] id = 1000 add_to_genesis = true From c0efa1fd05c5fc84aed969c7388bfcb9708dc736 Mon Sep 17 00:00:00 2001 From: Bruno Galvao Date: Wed, 24 May 2023 03:59:37 -0400 Subject: [PATCH 2/2] Add lockdown mode activation to lockdown-mode pallet's `GenesisConfig` (#176) * add activating lockdown mode to GenesisConfig * add activating lockdown mode to test ext * add tests for activating lockdown mode in GenesisConfig * cargo fmt -p pallet-lockdown-mode * fix linting * add activated to benchmark * removing, as this is already in another test * setting initial state to true, therefore no longer need to manually change state * rename activated in genesisConfig to initial_status --- pallets/lockdown-mode/src/benchmarking.rs | 4 +-- pallets/lockdown-mode/src/lib.rs | 15 ++++++++--- pallets/lockdown-mode/src/mock.rs | 12 ++++++--- pallets/lockdown-mode/src/tests.rs | 32 ++++++++++++++++------- 4 files changed, 43 insertions(+), 20 deletions(-) diff --git a/pallets/lockdown-mode/src/benchmarking.rs b/pallets/lockdown-mode/src/benchmarking.rs index 9f98ead7..5834ebad 100644 --- a/pallets/lockdown-mode/src/benchmarking.rs +++ b/pallets/lockdown-mode/src/benchmarking.rs @@ -14,12 +14,12 @@ benchmarks! { assert_eq!(LockdownModeStatus::::get(), ACTIVATED); } - deactivate_lockdown_mode { + deactivate_lockdown_mode { LockdownModeStatus::::put(ACTIVATED); }: deactivate_lockdown_mode(RawOrigin::Root) verify { assert_eq!(LockdownModeStatus::::get(), DEACTIVATED); } - impl_benchmark_test_suite!(LockdownMode, crate::mock::new_test_ext(), crate::mock::Test); + impl_benchmark_test_suite!(LockdownMode, crate::mock::new_test_ext(true), crate::mock::Test); } diff --git a/pallets/lockdown-mode/src/lib.rs b/pallets/lockdown-mode/src/lib.rs index dd035e32..de0ce6ba 100644 --- a/pallets/lockdown-mode/src/lib.rs +++ b/pallets/lockdown-mode/src/lib.rs @@ -34,15 +34,22 @@ pub mod pallet { #[pallet::pallet] pub struct Pallet(_); - #[derive(Default)] #[pallet::genesis_config] - /// Genesis config for lockdown mode pallet - pub struct GenesisConfig {} + pub struct GenesisConfig { + pub initial_status: bool, + } + + #[cfg(feature = "std")] + impl Default for GenesisConfig { + fn default() -> Self { + Self { initial_status: ACTIVATED } + } + } #[pallet::genesis_build] impl GenesisBuild for GenesisConfig { fn build(&self) { - LockdownModeStatus::::put(ACTIVATED); + LockdownModeStatus::::put(&self.initial_status); } } diff --git a/pallets/lockdown-mode/src/mock.rs b/pallets/lockdown-mode/src/mock.rs index e685a875..4ec21988 100644 --- a/pallets/lockdown-mode/src/mock.rs +++ b/pallets/lockdown-mode/src/mock.rs @@ -1,7 +1,7 @@ use crate as pallet_lockdown_mode; use cumulus_primitives_core::{relay_chain::BlockNumber as RelayBlockNumber, DmpMessageHandler}; use frame_support::{ - traits::{ConstU16, ConstU64, Contains}, + traits::{ConstU16, ConstU64, Contains, GenesisBuild}, weights::Weight, }; use frame_system as system; @@ -119,8 +119,12 @@ impl pallet_lockdown_mode::Config for Test { type WeightInfo = pallet_lockdown_mode::weights::SubstrateWeight; } -// Build genesis storage according to the mock runtime. -pub fn new_test_ext() -> sp_io::TestExternalities { - let storage = system::GenesisConfig::default().build_storage::().unwrap(); +pub fn new_test_ext(initial_status: bool) -> sp_io::TestExternalities { + let mut storage = system::GenesisConfig::default().build_storage::().unwrap(); + GenesisBuild::::assimilate_storage( + &pallet_lockdown_mode::GenesisConfig { initial_status }, + &mut storage, + ) + .unwrap(); storage.into() } diff --git a/pallets/lockdown-mode/src/tests.rs b/pallets/lockdown-mode/src/tests.rs index cf466b52..24b641cb 100644 --- a/pallets/lockdown-mode/src/tests.rs +++ b/pallets/lockdown-mode/src/tests.rs @@ -1,13 +1,28 @@ -use super::*; +use super::{GenesisConfig, *}; use crate::{mock::*, Error, ACTIVATED, DEACTIVATED}; use frame_support::{assert_noop, assert_ok, traits::Contains}; use pallet_balances::{self, Call as BalancesCall}; use pallet_remark::{self, Call as RemarkCall}; +#[test] +fn genesis_config_default() { + let default_genesis = GenesisConfig::default(); + assert_eq!(default_genesis.initial_status, ACTIVATED); +} + +#[test] +fn genesis_config_initialized() { + [true, false].into_iter().for_each(|expected| { + new_test_ext(expected).execute_with(|| { + let lockdown_mode = LockdownModeStatus::::get(); + assert_eq!(lockdown_mode, expected); + }); + }); +} + #[test] fn activate_lockdown_mode_works() { - new_test_ext().execute_with(|| { - assert_eq!(LockdownModeStatus::::get(), DEACTIVATED); + new_test_ext(false).execute_with(|| { assert_ok!(LockdownMode::activate_lockdown_mode(RuntimeOrigin::root())); let lockdown_mode = LockdownModeStatus::::get(); @@ -22,10 +37,7 @@ fn activate_lockdown_mode_works() { #[test] fn deactivate_lockdown_mode_works() { - new_test_ext().execute_with(|| { - // We activate lockdown mode first so we can deactivate it. - assert_ok!(LockdownMode::activate_lockdown_mode(RuntimeOrigin::root())); - + new_test_ext(true).execute_with(|| { assert_ok!(LockdownMode::deactivate_lockdown_mode(RuntimeOrigin::root())); let lockdown_mode = LockdownModeStatus::::get(); @@ -40,7 +52,7 @@ fn deactivate_lockdown_mode_works() { #[test] fn call_not_filtered_in_lockdown_mode() { - new_test_ext().execute_with(|| { + new_test_ext(false).execute_with(|| { assert_ok!(LockdownMode::activate_lockdown_mode(RuntimeOrigin::root())); let remark_call = RuntimeCall::Remark(RemarkCall::store { remark: vec![1, 2, 3] }); let result: bool = LockdownMode::contains(&remark_call); @@ -50,7 +62,7 @@ fn call_not_filtered_in_lockdown_mode() { #[test] fn call_filtered_in_lockdown_mode() { - new_test_ext().execute_with(|| { + new_test_ext(false).execute_with(|| { assert_ok!(LockdownMode::activate_lockdown_mode(RuntimeOrigin::root())); let balance_call = RuntimeCall::Balance(BalancesCall::transfer { dest: 1, value: 2 }); @@ -61,7 +73,7 @@ fn call_filtered_in_lockdown_mode() { #[test] fn call_not_filtered_in_normal_mode() { - new_test_ext().execute_with(|| { + new_test_ext(false).execute_with(|| { let lockdown_mode = LockdownModeStatus::::get(); assert_eq!(lockdown_mode, DEACTIVATED); let balance_call = RuntimeCall::Balance(BalancesCall::transfer { dest: 1, value: 2 });