From f8ff3c91427cf9d509336a0bf6dda3e32b2dd05e Mon Sep 17 00:00:00 2001
From: Serban Iorga <serban@parity.io>
Date: Tue, 28 Jun 2022 17:39:07 +0300
Subject: [PATCH] Unify the operating mode for bridge pallets (#1483)

Unify the operating mode for bridge pallets

- define the OperationMode trait and BasicOperatingMode enum
- use the OperationMode trait in all the bridge pallets
- use BasicOperatingMode instead of IsHalted for the Grandpa pallet
- use BasicOperatingMode as part of MessagesOperatingMode

Signed-off-by: Serban Iorga <serban@parity.io>
---
 bridges/modules/grandpa/src/benchmarking.rs   |   3 +-
 bridges/modules/grandpa/src/lib.rs            | 119 ++++++++++++------
 bridges/modules/messages/src/lib.rs           |  79 +++++++-----
 bridges/modules/parachains/src/lib.rs         |   3 +-
 bridges/primitives/header-chain/src/lib.rs    |   5 +-
 .../header-chain/src/storage_keys.rs          |  14 +--
 bridges/primitives/messages/src/lib.rs        |  22 ++--
 bridges/primitives/runtime/Cargo.toml         |   2 +
 bridges/primitives/runtime/src/lib.rs         |  44 +++++--
 .../src/finality/engine.rs                    |  22 ++--
 .../src/messages_source.rs                    |   9 +-
 11 files changed, 218 insertions(+), 104 deletions(-)

diff --git a/bridges/modules/grandpa/src/benchmarking.rs b/bridges/modules/grandpa/src/benchmarking.rs
index f12894bb5cb..655cd6eae74 100644
--- a/bridges/modules/grandpa/src/benchmarking.rs
+++ b/bridges/modules/grandpa/src/benchmarking.rs
@@ -41,6 +41,7 @@
 
 use crate::*;
 
+use bp_runtime::BasicOperatingMode;
 use bp_test_utils::{
 	accounts, make_justification_for_header, JustificationGeneratorParams, TEST_GRANDPA_ROUND,
 	TEST_GRANDPA_SET_ID,
@@ -84,7 +85,7 @@ fn prepare_benchmark_data<T: Config<I>, I: 'static>(
 		header: Box::new(bp_test_utils::test_header(Zero::zero())),
 		authority_list,
 		set_id: TEST_GRANDPA_SET_ID,
-		is_halted: false,
+		operating_mode: BasicOperatingMode::Normal,
 	};
 
 	bootstrap_bridge::<T, I>(init_data);
diff --git a/bridges/modules/grandpa/src/lib.rs b/bridges/modules/grandpa/src/lib.rs
index 726de9c507e..056aee406d8 100644
--- a/bridges/modules/grandpa/src/lib.rs
+++ b/bridges/modules/grandpa/src/lib.rs
@@ -71,6 +71,7 @@ pub type BridgedHeader<T, I> = HeaderOf<<T as Config<I>>::BridgedChain>;
 #[frame_support::pallet]
 pub mod pallet {
 	use super::*;
+	use bp_runtime::BasicOperatingMode;
 	use frame_support::pallet_prelude::*;
 	use frame_system::pallet_prelude::*;
 
@@ -119,14 +120,9 @@ pub mod pallet {
 
 	impl<T: Config<I>, I: 'static> OwnedBridgeModule<T> for Pallet<T, I> {
 		const LOG_TARGET: &'static str = "runtime::bridge-grandpa";
-		const OPERATING_MODE_KEY: &'static str = "IsHalted";
 		type OwnerStorage = PalletOwner<T, I>;
-		type OperatingMode = bool;
-		type OperatingModeStorage = IsHalted<T, I>;
-
-		fn is_halted() -> bool {
-			Self::OperatingModeStorage::get()
-		}
+		type OperatingMode = BasicOperatingMode;
+		type OperatingModeStorage = PalletOperatingMode<T, I>;
 	}
 
 	#[pallet::call]
@@ -237,8 +233,11 @@ pub mod pallet {
 		///
 		/// May only be called either by root, or by `PalletOwner`.
 		#[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))]
-		pub fn set_operational(origin: OriginFor<T>, operational: bool) -> DispatchResult {
-			Self::set_operating_mode(origin, !operational)
+		pub fn set_operating_mode(
+			origin: OriginFor<T>,
+			operating_mode: BasicOperatingMode,
+		) -> DispatchResult {
+			<Self as OwnedBridgeModule<_>>::set_operating_mode(origin, operating_mode)
 		}
 	}
 
@@ -293,9 +292,12 @@ pub mod pallet {
 	pub type PalletOwner<T: Config<I>, I: 'static = ()> =
 		StorageValue<_, T::AccountId, OptionQuery>;
 
-	/// If true, all pallet transactions are failed immediately.
+	/// The current operating mode of the pallet.
+	///
+	/// Depending on the mode either all, or no transactions will be allowed.
 	#[pallet::storage]
-	pub type IsHalted<T: Config<I>, I: 'static = ()> = StorageValue<_, bool, ValueQuery>;
+	pub type PalletOperatingMode<T: Config<I>, I: 'static = ()> =
+		StorageValue<_, BasicOperatingMode, ValueQuery>;
 
 	#[pallet::genesis_config]
 	pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
@@ -324,7 +326,7 @@ pub mod pallet {
 			} else {
 				// Since the bridge hasn't been initialized we shouldn't allow anyone to perform
 				// transactions.
-				<IsHalted<T, I>>::put(true);
+				<PalletOperatingMode<T, I>>::put(BasicOperatingMode::Halted);
 			}
 		}
 	}
@@ -463,7 +465,8 @@ pub mod pallet {
 	pub(crate) fn initialize_bridge<T: Config<I>, I: 'static>(
 		init_params: super::InitializationData<BridgedHeader<T, I>>,
 	) {
-		let super::InitializationData { header, authority_list, set_id, is_halted } = init_params;
+		let super::InitializationData { header, authority_list, set_id, operating_mode } =
+			init_params;
 
 		let initial_hash = header.hash();
 		<InitialHash<T, I>>::put(initial_hash);
@@ -473,7 +476,7 @@ pub mod pallet {
 		let authority_set = bp_header_chain::AuthoritySet::new(authority_list, set_id);
 		<CurrentAuthoritySet<T, I>>::put(authority_set);
 
-		<IsHalted<T, I>>::put(is_halted);
+		<PalletOperatingMode<T, I>>::put(operating_mode);
 	}
 
 	#[cfg(feature = "runtime-benchmarks")]
@@ -576,7 +579,7 @@ pub fn initialize_for_benchmarks<T: Config<I>, I: 'static>(header: BridgedHeader
 		authority_list: sp_std::vec::Vec::new(), /* we don't verify any proofs in external
 		                                          * benchmarks */
 		set_id: 0,
-		is_halted: false,
+		operating_mode: bp_runtime::BasicOperatingMode::Normal,
 	});
 }
 
@@ -584,6 +587,7 @@ pub fn initialize_for_benchmarks<T: Config<I>, I: 'static>(header: BridgedHeader
 mod tests {
 	use super::*;
 	use crate::mock::{run_test, test_header, Origin, TestHeader, TestNumber, TestRuntime};
+	use bp_runtime::BasicOperatingMode;
 	use bp_test_utils::{
 		authority_list, make_default_justification, make_justification_for_header,
 		JustificationGeneratorParams, ALICE, BOB,
@@ -611,7 +615,7 @@ mod tests {
 			header: Box::new(genesis),
 			authority_list: authority_list(),
 			set_id: 1,
-			is_halted: false,
+			operating_mode: BasicOperatingMode::Normal,
 		};
 
 		Pallet::<TestRuntime>::initialize(origin, init_data.clone()).map(|_| init_data)
@@ -685,7 +689,7 @@ mod tests {
 				CurrentAuthoritySet::<TestRuntime>::get().authorities,
 				init_data.authority_list
 			);
-			assert!(!IsHalted::<TestRuntime>::get());
+			assert_eq!(PalletOperatingMode::<TestRuntime>::get(), BasicOperatingMode::Normal);
 		})
 	}
 
@@ -707,29 +711,50 @@ mod tests {
 
 			assert_ok!(Pallet::<TestRuntime>::set_owner(Origin::root(), Some(1)));
 			assert_noop!(
-				Pallet::<TestRuntime>::set_operational(Origin::signed(2), false),
+				Pallet::<TestRuntime>::set_operating_mode(
+					Origin::signed(2),
+					BasicOperatingMode::Halted
+				),
 				DispatchError::BadOrigin,
 			);
-			assert_ok!(Pallet::<TestRuntime>::set_operational(Origin::root(), false));
+			assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
+				Origin::root(),
+				BasicOperatingMode::Halted
+			));
 
 			assert_ok!(Pallet::<TestRuntime>::set_owner(Origin::signed(1), None));
 			assert_noop!(
-				Pallet::<TestRuntime>::set_operational(Origin::signed(1), true),
+				Pallet::<TestRuntime>::set_operating_mode(
+					Origin::signed(1),
+					BasicOperatingMode::Normal
+				),
 				DispatchError::BadOrigin,
 			);
 			assert_noop!(
-				Pallet::<TestRuntime>::set_operational(Origin::signed(2), true),
+				Pallet::<TestRuntime>::set_operating_mode(
+					Origin::signed(2),
+					BasicOperatingMode::Normal
+				),
 				DispatchError::BadOrigin,
 			);
-			assert_ok!(Pallet::<TestRuntime>::set_operational(Origin::root(), true));
+			assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
+				Origin::root(),
+				BasicOperatingMode::Normal
+			));
 		});
 	}
 
 	#[test]
 	fn pallet_may_be_halted_by_root() {
 		run_test(|| {
-			assert_ok!(Pallet::<TestRuntime>::set_operational(Origin::root(), false));
-			assert_ok!(Pallet::<TestRuntime>::set_operational(Origin::root(), true));
+			assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
+				Origin::root(),
+				BasicOperatingMode::Halted
+			));
+			assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
+				Origin::root(),
+				BasicOperatingMode::Normal
+			));
 		});
 	}
 
@@ -738,21 +763,39 @@ mod tests {
 		run_test(|| {
 			PalletOwner::<TestRuntime>::put(2);
 
-			assert_ok!(Pallet::<TestRuntime>::set_operational(Origin::signed(2), false));
-			assert_ok!(Pallet::<TestRuntime>::set_operational(Origin::signed(2), true));
+			assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
+				Origin::signed(2),
+				BasicOperatingMode::Halted
+			));
+			assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
+				Origin::signed(2),
+				BasicOperatingMode::Normal
+			));
 
 			assert_noop!(
-				Pallet::<TestRuntime>::set_operational(Origin::signed(1), false),
+				Pallet::<TestRuntime>::set_operating_mode(
+					Origin::signed(1),
+					BasicOperatingMode::Halted
+				),
 				DispatchError::BadOrigin,
 			);
 			assert_noop!(
-				Pallet::<TestRuntime>::set_operational(Origin::signed(1), true),
+				Pallet::<TestRuntime>::set_operating_mode(
+					Origin::signed(1),
+					BasicOperatingMode::Normal
+				),
 				DispatchError::BadOrigin,
 			);
 
-			assert_ok!(Pallet::<TestRuntime>::set_operational(Origin::signed(2), false));
+			assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
+				Origin::signed(2),
+				BasicOperatingMode::Halted
+			));
 			assert_noop!(
-				Pallet::<TestRuntime>::set_operational(Origin::signed(1), true),
+				Pallet::<TestRuntime>::set_operating_mode(
+					Origin::signed(1),
+					BasicOperatingMode::Normal
+				),
 				DispatchError::BadOrigin,
 			);
 		});
@@ -763,13 +806,19 @@ mod tests {
 		run_test(|| {
 			initialize_substrate_bridge();
 
-			assert_ok!(Pallet::<TestRuntime>::set_operational(Origin::root(), false));
+			assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
+				Origin::root(),
+				BasicOperatingMode::Halted
+			));
 			assert_noop!(
 				submit_finality_proof(1),
 				Error::<TestRuntime>::BridgeModule(bp_runtime::OwnedBridgeModuleError::Halted)
 			);
 
-			assert_ok!(Pallet::<TestRuntime>::set_operational(Origin::root(), true));
+			assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
+				Origin::root(),
+				BasicOperatingMode::Normal
+			));
 			assert_ok!(submit_finality_proof(1));
 		})
 	}
@@ -851,7 +900,7 @@ mod tests {
 				header: Box::new(genesis),
 				authority_list: invalid_authority_list,
 				set_id: 1,
-				is_halted: false,
+				operating_mode: BasicOperatingMode::Normal,
 			};
 
 			assert_ok!(Pallet::<TestRuntime>::initialize(Origin::root(), init_data));
@@ -1115,8 +1164,8 @@ mod tests {
 	#[test]
 	fn storage_keys_computed_properly() {
 		assert_eq!(
-			IsHalted::<TestRuntime>::storage_value_final_key().to_vec(),
-			bp_header_chain::storage_keys::is_halted_key("Grandpa").0,
+			PalletOperatingMode::<TestRuntime>::storage_value_final_key().to_vec(),
+			bp_header_chain::storage_keys::pallet_operating_mode_key("Grandpa").0,
 		);
 
 		assert_eq!(
diff --git a/bridges/modules/messages/src/lib.rs b/bridges/modules/messages/src/lib.rs
index 78f44e75027..eed718477be 100644
--- a/bridges/modules/messages/src/lib.rs
+++ b/bridges/modules/messages/src/lib.rs
@@ -57,11 +57,11 @@ use bp_messages::{
 		DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages, SourceHeaderChain,
 	},
 	total_unrewarded_messages, DeliveredMessages, InboundLaneData, InboundMessageDetails, LaneId,
-	MessageData, MessageKey, MessageNonce, MessagePayload, OperatingMode, OutboundLaneData,
+	MessageData, MessageKey, MessageNonce, MessagePayload, MessagesOperatingMode, OutboundLaneData,
 	OutboundMessageDetails, Parameter as MessagesParameter, UnrewardedRelayer,
 	UnrewardedRelayersState,
 };
-use bp_runtime::{ChainId, OwnedBridgeModule, Size};
+use bp_runtime::{BasicOperatingMode, ChainId, OwnedBridgeModule, Size};
 use codec::{Decode, Encode};
 use frame_support::{
 	fail,
@@ -218,14 +218,9 @@ pub mod pallet {
 
 	impl<T: Config<I>, I: 'static> OwnedBridgeModule<T> for Pallet<T, I> {
 		const LOG_TARGET: &'static str = "runtime::bridge-messages";
-		const OPERATING_MODE_KEY: &'static str = "PalletOperatingMode";
 		type OwnerStorage = PalletOwner<T, I>;
-		type OperatingMode = OperatingMode;
+		type OperatingMode = MessagesOperatingMode;
 		type OperatingModeStorage = PalletOperatingMode<T, I>;
-
-		fn is_halted() -> bool {
-			Self::OperatingModeStorage::get() == OperatingMode::Halted
-		}
 	}
 
 	#[pallet::call]
@@ -244,7 +239,7 @@ pub mod pallet {
 		#[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))]
 		pub fn set_operating_mode(
 			origin: OriginFor<T>,
-			operating_mode: OperatingMode,
+			operating_mode: MessagesOperatingMode,
 		) -> DispatchResult {
 			<Self as OwnedBridgeModule<_>>::set_operating_mode(origin, operating_mode)
 		}
@@ -708,7 +703,7 @@ pub mod pallet {
 	#[pallet::storage]
 	#[pallet::getter(fn operating_mode)]
 	pub type PalletOperatingMode<T: Config<I>, I: 'static = ()> =
-		StorageValue<_, OperatingMode, ValueQuery>;
+		StorageValue<_, MessagesOperatingMode, ValueQuery>;
 
 	/// Map of lane id => inbound lane data.
 	#[pallet::storage]
@@ -728,7 +723,7 @@ pub mod pallet {
 	#[pallet::genesis_config]
 	pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
 		/// Initial pallet operating mode.
-		pub operating_mode: OperatingMode,
+		pub operating_mode: MessagesOperatingMode,
 		/// Initial pallet owner.
 		pub owner: Option<T::AccountId>,
 		/// Dummy marker.
@@ -972,11 +967,13 @@ where
 
 /// Ensure that the pallet is in normal operational mode.
 fn ensure_normal_operating_mode<T: Config<I>, I: 'static>() -> Result<(), Error<T, I>> {
-	if PalletOperatingMode::<T, I>::get() != OperatingMode::Normal {
-		Err(Error::<T, I>::NotOperatingNormally)
-	} else {
-		Ok(())
+	if PalletOperatingMode::<T, I>::get() ==
+		MessagesOperatingMode::Basic(BasicOperatingMode::Normal)
+	{
+		return Ok(())
 	}
+
+	Err(Error::<T, I>::NotOperatingNormally)
 }
 
 /// Creates new inbound lane object, backed by runtime storage.
@@ -1234,26 +1231,35 @@ mod tests {
 
 			assert_ok!(Pallet::<TestRuntime>::set_owner(Origin::root(), Some(1)));
 			assert_noop!(
-				Pallet::<TestRuntime>::set_operating_mode(Origin::signed(2), OperatingMode::Halted),
+				Pallet::<TestRuntime>::set_operating_mode(
+					Origin::signed(2),
+					MessagesOperatingMode::Basic(BasicOperatingMode::Halted)
+				),
 				DispatchError::BadOrigin,
 			);
 			assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
 				Origin::root(),
-				OperatingMode::Halted
+				MessagesOperatingMode::Basic(BasicOperatingMode::Halted)
 			));
 
 			assert_ok!(Pallet::<TestRuntime>::set_owner(Origin::signed(1), None));
 			assert_noop!(
-				Pallet::<TestRuntime>::set_operating_mode(Origin::signed(1), OperatingMode::Normal),
+				Pallet::<TestRuntime>::set_operating_mode(
+					Origin::signed(1),
+					MessagesOperatingMode::Basic(BasicOperatingMode::Normal)
+				),
 				DispatchError::BadOrigin,
 			);
 			assert_noop!(
-				Pallet::<TestRuntime>::set_operating_mode(Origin::signed(2), OperatingMode::Normal),
+				Pallet::<TestRuntime>::set_operating_mode(
+					Origin::signed(2),
+					MessagesOperatingMode::Basic(BasicOperatingMode::Normal)
+				),
 				DispatchError::BadOrigin,
 			);
 			assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
 				Origin::root(),
-				OperatingMode::Normal
+				MessagesOperatingMode::Basic(BasicOperatingMode::Normal)
 			));
 		});
 	}
@@ -1263,11 +1269,11 @@ mod tests {
 		run_test(|| {
 			assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
 				Origin::root(),
-				OperatingMode::Halted
+				MessagesOperatingMode::Basic(BasicOperatingMode::Halted)
 			));
 			assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
 				Origin::root(),
-				OperatingMode::Normal
+				MessagesOperatingMode::Basic(BasicOperatingMode::Normal)
 			));
 		});
 	}
@@ -1279,28 +1285,37 @@ mod tests {
 
 			assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
 				Origin::signed(2),
-				OperatingMode::Halted
+				MessagesOperatingMode::Basic(BasicOperatingMode::Halted)
 			));
 			assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
 				Origin::signed(2),
-				OperatingMode::Normal
+				MessagesOperatingMode::Basic(BasicOperatingMode::Normal)
 			));
 
 			assert_noop!(
-				Pallet::<TestRuntime>::set_operating_mode(Origin::signed(1), OperatingMode::Halted),
+				Pallet::<TestRuntime>::set_operating_mode(
+					Origin::signed(1),
+					MessagesOperatingMode::Basic(BasicOperatingMode::Halted)
+				),
 				DispatchError::BadOrigin,
 			);
 			assert_noop!(
-				Pallet::<TestRuntime>::set_operating_mode(Origin::signed(1), OperatingMode::Normal),
+				Pallet::<TestRuntime>::set_operating_mode(
+					Origin::signed(1),
+					MessagesOperatingMode::Basic(BasicOperatingMode::Normal)
+				),
 				DispatchError::BadOrigin,
 			);
 
 			assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
 				Origin::signed(2),
-				OperatingMode::Halted
+				MessagesOperatingMode::Basic(BasicOperatingMode::Halted)
 			));
 			assert_noop!(
-				Pallet::<TestRuntime>::set_operating_mode(Origin::signed(1), OperatingMode::Normal),
+				Pallet::<TestRuntime>::set_operating_mode(
+					Origin::signed(1),
+					MessagesOperatingMode::Basic(BasicOperatingMode::Normal)
+				),
 				DispatchError::BadOrigin,
 			);
 		});
@@ -1408,7 +1423,9 @@ mod tests {
 			// send message first to be able to check that delivery_proof fails later
 			send_regular_message();
 
-			PalletOperatingMode::<TestRuntime, ()>::put(OperatingMode::Halted);
+			PalletOperatingMode::<TestRuntime, ()>::put(MessagesOperatingMode::Basic(
+				BasicOperatingMode::Halted,
+			));
 
 			assert_noop!(
 				Pallet::<TestRuntime>::send_message(
@@ -1466,7 +1483,9 @@ mod tests {
 			// send message first to be able to check that delivery_proof fails later
 			send_regular_message();
 
-			PalletOperatingMode::<TestRuntime, ()>::put(OperatingMode::RejectingOutboundMessages);
+			PalletOperatingMode::<TestRuntime, ()>::put(
+				MessagesOperatingMode::RejectingOutboundMessages,
+			);
 
 			assert_noop!(
 				Pallet::<TestRuntime>::send_message(
diff --git a/bridges/modules/parachains/src/lib.rs b/bridges/modules/parachains/src/lib.rs
index cf634e5b5ca..46be3866db1 100644
--- a/bridges/modules/parachains/src/lib.rs
+++ b/bridges/modules/parachains/src/lib.rs
@@ -400,6 +400,7 @@ mod tests {
 		run_test, test_relay_header, Origin, TestRuntime, PARAS_PALLET_NAME, UNTRACKED_PARACHAIN_ID,
 	};
 
+	use bp_runtime::BasicOperatingMode;
 	use bp_test_utils::{authority_list, make_default_justification};
 	use frame_support::{
 		assert_noop, assert_ok,
@@ -420,7 +421,7 @@ mod tests {
 				header: Box::new(test_relay_header(0, state_root)),
 				authority_list: authority_list(),
 				set_id: 1,
-				is_halted: false,
+				operating_mode: BasicOperatingMode::Normal,
 			},
 		)
 		.unwrap();
diff --git a/bridges/primitives/header-chain/src/lib.rs b/bridges/primitives/header-chain/src/lib.rs
index ff8ee82f41e..8ac7972621c 100644
--- a/bridges/primitives/header-chain/src/lib.rs
+++ b/bridges/primitives/header-chain/src/lib.rs
@@ -19,6 +19,7 @@
 
 #![cfg_attr(not(feature = "std"), no_std)]
 
+use bp_runtime::BasicOperatingMode;
 use codec::{Codec, Decode, Encode, EncodeLike};
 use core::{clone::Clone, cmp::Eq, default::Default, fmt::Debug};
 use scale_info::TypeInfo;
@@ -66,8 +67,8 @@ pub struct InitializationData<H: HeaderT> {
 	pub authority_list: AuthorityList,
 	/// The ID of the initial authority set.
 	pub set_id: SetId,
-	/// Should the pallet block transaction immediately after initialization.
-	pub is_halted: bool,
+	/// Pallet operating mode.
+	pub operating_mode: BasicOperatingMode,
 }
 
 /// base trait for verifying transaction inclusion proofs.
diff --git a/bridges/primitives/header-chain/src/storage_keys.rs b/bridges/primitives/header-chain/src/storage_keys.rs
index 14c40d69853..bb642b1817f 100644
--- a/bridges/primitives/header-chain/src/storage_keys.rs
+++ b/bridges/primitives/header-chain/src/storage_keys.rs
@@ -17,18 +17,18 @@
 //! Storage keys of bridge GRANDPA pallet.
 
 /// Name of the `IsHalted` storage value.
-pub const IS_HALTED_VALUE_NAME: &str = "IsHalted";
+pub const PALLET_OPERATING_MODE_VALUE_NAME: &str = "PalletOperatingMode";
 /// Name of the `BestFinalized` storage value.
 pub const BEST_FINALIZED_VALUE_NAME: &str = "BestFinalized";
 
 use sp_core::storage::StorageKey;
 
-/// Storage key of the `IsHalted` flag in the runtime storage.
-pub fn is_halted_key(pallet_prefix: &str) -> StorageKey {
+/// Storage key of the `PalletOperatingMode` variable in the runtime storage.
+pub fn pallet_operating_mode_key(pallet_prefix: &str) -> StorageKey {
 	StorageKey(
 		bp_runtime::storage_value_final_key(
 			pallet_prefix.as_bytes(),
-			IS_HALTED_VALUE_NAME.as_bytes(),
+			PALLET_OPERATING_MODE_VALUE_NAME.as_bytes(),
 		)
 		.to_vec(),
 	)
@@ -51,13 +51,13 @@ mod tests {
 	use hex_literal::hex;
 
 	#[test]
-	fn is_halted_key_computed_properly() {
+	fn pallet_operating_mode_key_computed_properly() {
 		// If this test fails, then something has been changed in module storage that is breaking
 		// compatibility with previous pallet.
-		let storage_key = is_halted_key("BridgeGrandpa").0;
+		let storage_key = pallet_operating_mode_key("BridgeGrandpa").0;
 		assert_eq!(
 			storage_key,
-			hex!("0b06f475eddb98cf933a12262e0388de9611a984bbd04e2fd39f97bbc006115f").to_vec(),
+			hex!("0b06f475eddb98cf933a12262e0388de0f4cf0917788d791142ff6c1f216e7b3").to_vec(),
 			"Unexpected storage key: {}",
 			hex::encode(&storage_key),
 		);
diff --git a/bridges/primitives/messages/src/lib.rs b/bridges/primitives/messages/src/lib.rs
index 8d51a6fa864..455caad729b 100644
--- a/bridges/primitives/messages/src/lib.rs
+++ b/bridges/primitives/messages/src/lib.rs
@@ -32,14 +32,15 @@ pub mod storage_keys;
 pub mod target_chain;
 
 // Weight is reexported to avoid additional frame-support dependencies in related crates.
+use bp_runtime::{BasicOperatingMode, OperatingMode};
 pub use frame_support::weights::Weight;
 
 /// Messages pallet operating mode.
 #[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)]
 #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
-pub enum OperatingMode {
-	/// Normal mode, when all operations are allowed.
-	Normal,
+pub enum MessagesOperatingMode {
+	/// Basic operating mode (Normal/Halted)
+	Basic(BasicOperatingMode),
 	/// The pallet is not accepting outbound messages. Inbound messages and receiving proofs
 	/// are still accepted.
 	///
@@ -48,13 +49,20 @@ pub enum OperatingMode {
 	/// queued messages to the bridged chain. Once upgrade is completed, the mode may be switched
 	/// back to `Normal`.
 	RejectingOutboundMessages,
-	/// The pallet is halted. All operations (except operating mode change) are prohibited.
-	Halted,
 }
 
-impl Default for OperatingMode {
+impl Default for MessagesOperatingMode {
 	fn default() -> Self {
-		OperatingMode::Normal
+		MessagesOperatingMode::Basic(BasicOperatingMode::Normal)
+	}
+}
+
+impl OperatingMode for MessagesOperatingMode {
+	fn is_halted(&self) -> bool {
+		match self {
+			Self::Basic(operating_mode) => operating_mode.is_halted(),
+			_ => false,
+		}
 	}
 }
 
diff --git a/bridges/primitives/runtime/Cargo.toml b/bridges/primitives/runtime/Cargo.toml
index 691426aaf04..94dd12175d9 100644
--- a/bridges/primitives/runtime/Cargo.toml
+++ b/bridges/primitives/runtime/Cargo.toml
@@ -11,6 +11,7 @@ codec = { package = "parity-scale-codec", version = "3.0.0", default-features =
 hash-db = { version = "0.15.2", default-features = false }
 num-traits = { version = "0.2", default-features = false }
 scale-info = { version = "2.1.1", default-features = false, features = ["derive"] }
+serde = { version = "1.0", optional = true, features = ["derive"] }
 
 # Substrate Dependencies
 
@@ -35,6 +36,7 @@ std = [
 	"hash-db/std",
 	"num-traits/std",
 	"scale-info/std",
+	"serde",
 	"sp-core/std",
 	"sp-io/std",
 	"sp-runtime/std",
diff --git a/bridges/primitives/runtime/src/lib.rs b/bridges/primitives/runtime/src/lib.rs
index ee5e6b5d24e..cba89df7266 100644
--- a/bridges/primitives/runtime/src/lib.rs
+++ b/bridges/primitives/runtime/src/lib.rs
@@ -269,18 +269,47 @@ pub enum OwnedBridgeModuleError {
 	Halted,
 }
 
+/// Operating mode for a bridge module.
+pub trait OperatingMode: Send + Copy + Debug + FullCodec {
+	// Returns true if the bridge module is halted.
+	fn is_halted(&self) -> bool;
+}
+
+/// Basic operating modes for a bridges module (Normal/Halted).
+#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)]
+#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
+pub enum BasicOperatingMode {
+	/// Normal mode, when all operations are allowed.
+	Normal,
+	/// The pallet is halted. All operations (except operating mode change) are prohibited.
+	Halted,
+}
+
+impl Default for BasicOperatingMode {
+	fn default() -> Self {
+		Self::Normal
+	}
+}
+
+impl OperatingMode for BasicOperatingMode {
+	fn is_halted(&self) -> bool {
+		*self == BasicOperatingMode::Halted
+	}
+}
+
 /// Bridge module that has owner and operating mode
 pub trait OwnedBridgeModule<T: frame_system::Config> {
 	/// The target that will be used when publishing logs related to this module.
 	const LOG_TARGET: &'static str;
-	const OPERATING_MODE_KEY: &'static str;
 
 	type OwnerStorage: StorageValue<T::AccountId, Query = Option<T::AccountId>>;
-	type OperatingMode: Copy + Debug + FullCodec;
-	type OperatingModeStorage: StorageValue<Self::OperatingMode>;
+	type OperatingMode: OperatingMode;
+	type OperatingModeStorage: StorageValue<Self::OperatingMode, Query = Self::OperatingMode>;
 
 	/// Check if the module is halted.
-	fn is_halted() -> bool;
+	fn is_halted() -> bool {
+		Self::OperatingModeStorage::get().is_halted()
+	}
 
 	/// Ensure that the origin is either root, or `PalletOwner`.
 	fn ensure_owner_or_root(origin: T::Origin) -> Result<(), BadOrigin> {
@@ -325,12 +354,7 @@ pub trait OwnedBridgeModule<T: frame_system::Config> {
 	) -> DispatchResult {
 		Self::ensure_owner_or_root(origin)?;
 		Self::OperatingModeStorage::put(operating_mode);
-		log::info!(
-			target: Self::LOG_TARGET,
-			"Setting operating mode ( {} = {:?}).",
-			Self::OPERATING_MODE_KEY,
-			operating_mode
-		);
+		log::info!(target: Self::LOG_TARGET, "Setting operating mode to {:?}.", operating_mode);
 		Ok(())
 	}
 }
diff --git a/bridges/relays/lib-substrate-relay/src/finality/engine.rs b/bridges/relays/lib-substrate-relay/src/finality/engine.rs
index 0f2cc87660b..b2b72e4f2c3 100644
--- a/bridges/relays/lib-substrate-relay/src/finality/engine.rs
+++ b/bridges/relays/lib-substrate-relay/src/finality/engine.rs
@@ -23,6 +23,7 @@ use bp_header_chain::{
 	justification::{verify_justification, GrandpaJustification},
 	FinalityProof,
 };
+use bp_runtime::{BasicOperatingMode, OperatingMode};
 use codec::{Decode, Encode};
 use finality_grandpa::voter_set::VoterSet;
 use num_traits::{One, Zero};
@@ -44,10 +45,12 @@ pub trait Engine<C: Chain>: Send {
 	type FinalityProof: FinalityProof<BlockNumberOf<C>> + Decode + Encode;
 	/// Type of bridge pallet initialization data.
 	type InitializationData: std::fmt::Debug + Send + Sync + 'static;
+	/// Type of bridge pallet operating mode.
+	type OperatingMode: OperatingMode + 'static;
 
-	/// Returns storage key at the bridged (target) chain that corresponds to the `bool` value,
-	/// which is true when the bridge pallet is halted.
-	fn is_halted_key() -> StorageKey;
+	/// Returns storage key at the bridged (target) chain that corresponds to the variable
+	/// that holds the operating mode of the pallet.
+	fn pallet_operating_mode_key() -> StorageKey;
 	/// Returns storage at the bridged (target) chain that corresponds to some value that is
 	/// missing from the storage until bridge pallet is initialized.
 	///
@@ -74,7 +77,11 @@ pub trait Engine<C: Chain>: Send {
 	async fn is_halted<TargetChain: Chain>(
 		target_client: &Client<TargetChain>,
 	) -> Result<bool, SubstrateError> {
-		Ok(target_client.storage_value(Self::is_halted_key(), None).await?.unwrap_or(false))
+		Ok(target_client
+			.storage_value::<Self::OperatingMode>(Self::pallet_operating_mode_key(), None)
+			.await?
+			.map(|operating_mode| operating_mode.is_halted())
+			.unwrap_or(false))
 	}
 }
 
@@ -112,9 +119,10 @@ impl<C: ChainWithGrandpa> Engine<C> for Grandpa<C> {
 	const ID: ConsensusEngineId = sp_finality_grandpa::GRANDPA_ENGINE_ID;
 	type FinalityProof = GrandpaJustification<HeaderOf<C>>;
 	type InitializationData = bp_header_chain::InitializationData<C::Header>;
+	type OperatingMode = BasicOperatingMode;
 
-	fn is_halted_key() -> StorageKey {
-		bp_header_chain::storage_keys::is_halted_key(C::WITH_CHAIN_GRANDPA_PALLET_NAME)
+	fn pallet_operating_mode_key() -> StorageKey {
+		bp_header_chain::storage_keys::pallet_operating_mode_key(C::WITH_CHAIN_GRANDPA_PALLET_NAME)
 	}
 
 	fn is_initialized_key() -> StorageKey {
@@ -237,7 +245,7 @@ impl<C: ChainWithGrandpa> Engine<C> for Grandpa<C> {
 			} else {
 				initial_authorities_set_id
 			},
-			is_halted: false,
+			operating_mode: BasicOperatingMode::Normal,
 		})
 	}
 }
diff --git a/bridges/relays/lib-substrate-relay/src/messages_source.rs b/bridges/relays/lib-substrate-relay/src/messages_source.rs
index 0e21ef66eda..5a6b6554e4b 100644
--- a/bridges/relays/lib-substrate-relay/src/messages_source.rs
+++ b/bridges/relays/lib-substrate-relay/src/messages_source.rs
@@ -31,10 +31,10 @@ use async_std::sync::Arc;
 use async_trait::async_trait;
 use bp_messages::{
 	storage_keys::{operating_mode_key, outbound_lane_data_key},
-	InboundMessageDetails, LaneId, MessageData, MessageNonce, OperatingMode, OutboundLaneData,
-	OutboundMessageDetails, UnrewardedRelayersState,
+	InboundMessageDetails, LaneId, MessageData, MessageNonce, MessagesOperatingMode,
+	OutboundLaneData, OutboundMessageDetails, UnrewardedRelayersState,
 };
-use bp_runtime::messages::DispatchFeePayment;
+use bp_runtime::{messages::DispatchFeePayment, BasicOperatingMode};
 use bridge_runtime_common::messages::{
 	source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof,
 };
@@ -420,7 +420,8 @@ where
 	let operating_mode = client
 		.storage_value(operating_mode_key(WithChain::WITH_CHAIN_MESSAGES_PALLET_NAME), None)
 		.await?;
-	let is_halted = operating_mode == Some(OperatingMode::Halted);
+	let is_halted =
+		operating_mode == Some(MessagesOperatingMode::Basic(BasicOperatingMode::Halted));
 	if is_halted {
 		Err(SubstrateError::BridgePalletIsHalted)
 	} else {
-- 
GitLab