Crate pallet_safe_mode

source ·
Expand description

§Safe Mode

Trigger for stopping all extrinsics outside of a specific whitelist.

§WARNING

NOT YET AUDITED. DO NOT USE IN PRODUCTION.

§Pallet API

See the pallet module for more information about the interfaces this pallet exposes, including its configuration trait, dispatchables, storage items, events, and errors.

§Overview

Safe mode is entered via two paths (deposit or forced) until a set block number. The mode is exited when the block number is reached or a call to one of the exit extrinsics is made. A WhitelistedCalls configuration item contains all calls that can be executed while in safe mode.

§Primary Features

  • Entering safe mode can be via privileged origin or anyone who places a deposit.
  • Origin configuration items are separated for privileged entering and exiting safe mode.
  • A configurable duration sets the number of blocks after which the system will exit safe mode.
  • Safe mode may be extended beyond the configured exit by additional calls.

§Example

Configuration of call filters:

impl frame_system::Config for Runtime {
  // …
  type BaseCallFilter = InsideBoth<DefaultFilter, SafeMode>;
  // …
}

Entering safe mode with deposit:

#[test]
fn can_activate() {
	new_test_ext().execute_with(|| {
		assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0)));
		assert_eq!(
			EnteredUntil::<Test>::get().unwrap(),
			System::block_number() + mock::EnterDuration::get()
		);
		assert_eq!(Balances::reserved_balance(0), mock::EnterDepositAmount::get());
		assert_eq!(Notifications::get(), vec![(1, true)]);
		assert_noop!(SafeMode::enter(RuntimeOrigin::signed(0)), Error::<Test>::Entered);
		assert_eq!(Notifications::get(), vec![(1, true)]);
		// Assert the deposit.
		assert_eq!(Deposits::<Test>::get(0, 1), Some(mock::EnterDepositAmount::get()));
	});
}

Entering safe mode via privileged origin:

#[test]
fn can_force_activate_with_config_origin() {
	new_test_ext().execute_with(|| {
		assert_ok!(SafeMode::force_enter(signed(ForceEnterStrong::get())));
		assert_eq!(Notifications::get(), vec![(1, true)]);
		assert_eq!(
			EnteredUntil::<Test>::get().unwrap(),
			System::block_number() + ForceEnterStrong::get()
		);
		assert_noop!(
			SafeMode::force_enter(signed(ForceEnterStrong::get())),
			Error::<Test>::Entered
		);
		assert_eq!(Notifications::get(), vec![(1, true)]);
	});
}

Exiting safe mode via privileged origin:

#[test]
fn can_force_deactivate_with_config_origin() {
	new_test_ext().execute_with(|| {
		assert_eq!(EnteredUntil::<Test>::get(), None);
		assert_err!(
			SafeMode::force_exit(RuntimeOrigin::signed(ForceExitOrigin::get())),
			Error::<Test>::Exited
		);
		assert_ok!(SafeMode::force_enter(signed(ForceEnterWeak::get())));
		assert_ok!(SafeMode::force_exit(RuntimeOrigin::signed(ForceExitOrigin::get())));
		assert_eq!(Notifications::get(), vec![(1, true), (1, false)]);
	});
}

§Low Level / Implementation Details

§Use Cost

A storage value (EnteredUntil) is used to store the block safe mode will be exited on. Using the call filter will require a db read of that storage on the first extrinsic. The storage will be added to the overlay and incur low cost for all additional calls.

Re-exports§

Modules§

  • The pallet module in each FRAME pallet hosts the most important items needed to construct this pallet.
  • Autogenerated weights for pallet_safe_mode