Skip to content
This repository has been archived by the owner on May 21, 2024. It is now read-only.

Commit

Permalink
Backport Lockdown-pallet (#191)
Browse files Browse the repository at this point in the history
* lockdown-pallet backport

* 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

---------

Co-authored-by: Valentin Fernandez <valentin@parity.io>
Co-authored-by: Bruno Galvao <brunopgalvao@gmail.com>
  • Loading branch information
3 people authored May 26, 2023
1 parent 7ee2415 commit a6f4f90
Show file tree
Hide file tree
Showing 14 changed files with 723 additions and 11 deletions.
50 changes: 50 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions node/service/src/chain_spec/trappist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ fn testnet_genesis(
phantom: Default::default(),
},
treasury: Default::default(),
lockdown_mode: Default::default(),
}
}

Expand Down Expand Up @@ -349,5 +350,6 @@ fn trappist_live_genesis(
phantom: Default::default(),
},
treasury: Default::default(),
lockdown_mode: Default::default(),
}
}
77 changes: 77 additions & 0 deletions pallets/lockdown-mode/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"]
41 changes: 41 additions & 0 deletions pallets/lockdown-mode/README.md
Original file line number Diff line number Diff line change
@@ -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.
25 changes: 25 additions & 0 deletions pallets/lockdown-mode/src/benchmarking.rs
Original file line number Diff line number Diff line change
@@ -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::<T>::put(DEACTIVATED);
}: activate_lockdown_mode(RawOrigin::Root)
verify {
assert_eq!(LockdownModeStatus::<T>::get(), ACTIVATED);
}

deactivate_lockdown_mode {
LockdownModeStatus::<T>::put(ACTIVATED);
}: deactivate_lockdown_mode(RawOrigin::Root)
verify {
assert_eq!(LockdownModeStatus::<T>::get(), DEACTIVATED);
}

impl_benchmark_test_suite!(LockdownMode, crate::mock::new_test_ext(true), crate::mock::Test);
}
155 changes: 155 additions & 0 deletions pallets/lockdown-mode/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
#![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:
/// <https://docs.substrate.io/reference/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<T>(_);

#[pallet::genesis_config]
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<T: Config> GenesisBuild<T> for GenesisConfig {
fn build(&self) {
LockdownModeStatus::<T>::put(&self.initial_status);
}
}

#[pallet::config]
pub trait Config: frame_system::Config {
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
type LockdownModeOrigin: EnsureOrigin<Self::RuntimeOrigin>;
type BlackListedCalls: Contains<Self::RuntimeCall>;
type LockdownDmpHandler: DmpMessageHandler;
type XcmExecutorManager: PauseXcmExecution;
type WeightInfo: WeightInfo;
}

#[pallet::storage]
pub type LockdownModeStatus<T: Config> = StorageValue<_, bool, ValueQuery>;

#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
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<T> {
/// Lockdown mode was already activated
LockdownModeAlreadyActivated,
/// Lockdown mode was already deactivated
LockdownModeAlreadyDeactivated,
}

#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::call_index(0)]
#[pallet::weight(<T as pallet::Config>::WeightInfo::activate_lockdown_mode())]
pub fn activate_lockdown_mode(origin: OriginFor<T>) -> DispatchResult {
T::LockdownModeOrigin::ensure_origin(origin)?;

ensure!(!LockdownModeStatus::<T>::get(), Error::<T>::LockdownModeAlreadyActivated);

LockdownModeStatus::<T>::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(<T as pallet::Config>::WeightInfo::deactivate_lockdown_mode())]
pub fn deactivate_lockdown_mode(origin: OriginFor<T>) -> DispatchResult {
T::LockdownModeOrigin::ensure_origin(origin)?;
ensure!(LockdownModeStatus::<T>::get(), Error::<T>::LockdownModeAlreadyDeactivated);

LockdownModeStatus::<T>::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<T: Config> Contains<T::RuntimeCall> for Pallet<T> {
fn contains(call: &T::RuntimeCall) -> bool {
if LockdownModeStatus::<T>::get() {
T::BlackListedCalls::contains(call)
} else {
return true
}
}
}

impl<T: Config> DmpMessageHandler for Pallet<T> {
fn handle_dmp_messages(
iter: impl Iterator<Item = (RelayBlockNumber, Vec<u8>)>,
limit: Weight,
) -> Weight {
if LockdownModeStatus::<T>::get() {
T::LockdownDmpHandler::handle_dmp_messages(iter, Weight::zero())
} else {
// Normal path, everything should pass through
T::LockdownDmpHandler::handle_dmp_messages(iter, limit)
}
}
}
}
Loading

0 comments on commit a6f4f90

Please sign in to comment.