diff --git a/bridges/modules/parachains/src/lib.rs b/bridges/modules/parachains/src/lib.rs index a70672b612bb6e2e05f24debcdc8802436f9bb89..5601f588d50331a2548380a375b1ce95fd6acf85 100644 --- a/bridges/modules/parachains/src/lib.rs +++ b/bridges/modules/parachains/src/lib.rs @@ -80,6 +80,7 @@ struct UpdateParachainHeadArtifacts { #[frame_support::pallet] pub mod pallet { use super::*; + use bp_runtime::{BasicOperatingMode, OwnedBridgeModule}; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; @@ -100,6 +101,8 @@ pub mod pallet { StorageRootMismatch, /// Failed to extract state root from given parachain head. FailedToExtractStateRoot, + /// Error generated by the `OwnedBridgeModule` trait. + BridgeModule(bp_runtime::OwnedBridgeModuleError), } #[pallet::config] @@ -138,6 +141,23 @@ pub mod pallet { type HeadsToKeep: Get<u32>; } + /// Optional pallet owner. + /// + /// Pallet owner has a right to halt all pallet operations and then resume them. If it is + /// `None`, then there are no direct ways to halt/resume pallet operations, but other + /// runtime methods may still be used to do that (i.e. democracy::referendum to update halt + /// flag directly or call the `halt_operations`). + #[pallet::storage] + pub type PalletOwner<T: Config<I>, I: 'static = ()> = + StorageValue<_, T::AccountId, OptionQuery>; + + /// The current operating mode of the pallet. + /// + /// Depending on the mode either all, or no transactions will be allowed. + #[pallet::storage] + pub type PalletOperatingMode<T: Config<I>, I: 'static = ()> = + StorageValue<_, BasicOperatingMode, ValueQuery>; + /// Best parachain heads. #[pallet::storage] pub type BestParaHeads<T: Config<I>, I: 'static = ()> = @@ -158,6 +178,13 @@ pub mod pallet { #[pallet::without_storage_info] pub struct Pallet<T, I = ()>(PhantomData<(T, I)>); + impl<T: Config<I>, I: 'static> OwnedBridgeModule<T> for Pallet<T, I> { + const LOG_TARGET: &'static str = LOG_TARGET; + type OwnerStorage = PalletOwner<T, I>; + type OperatingMode = BasicOperatingMode; + type OperatingModeStorage = PalletOperatingMode<T, I>; + } + #[pallet::call] impl<T: Config<I>, I: 'static> Pallet<T, I> where @@ -185,6 +212,7 @@ pub mod pallet { parachains: Vec<ParaId>, parachain_heads_proof: ParaHeadsProof, ) -> DispatchResultWithPostInfo { + Self::ensure_not_halted().map_err(Error::<T, I>::BridgeModule)?; // we'll need relay chain header to verify that parachains heads are always increasing. let (relay_block_number, relay_block_hash) = at_relay_block; let relay_block = pallet_bridge_grandpa::ImportedHeaders::< @@ -266,6 +294,25 @@ pub mod pallet { Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee: Pays::Yes }) } + + /// Change `PalletOwner`. + /// + /// May only be called either by root, or by `PalletOwner`. + #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] + pub fn set_owner(origin: OriginFor<T>, new_owner: Option<T::AccountId>) -> DispatchResult { + <Self as OwnedBridgeModule<_>>::set_owner(origin, new_owner) + } + + /// Halt or resume all pallet operations. + /// + /// May only be called either by root, or by `PalletOwner`. + #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] + pub fn set_operating_mode( + origin: OriginFor<T>, + operating_mode: BasicOperatingMode, + ) -> DispatchResult { + <Self as OwnedBridgeModule<_>>::set_operating_mode(origin, operating_mode) + } } impl<T: Config<I>, I: 'static> Pallet<T, I> { @@ -404,7 +451,9 @@ mod tests { }; use bp_runtime::BasicOperatingMode; - use bp_test_utils::{authority_list, make_default_justification}; + use bp_test_utils::{ + authority_list, generate_owned_bridge_module_tests, make_default_justification, + }; use frame_support::{ assert_noop, assert_ok, dispatch::DispatchResultWithPostInfo, @@ -412,6 +461,7 @@ mod tests { traits::{Get, OnInitialize}, weights::Weight, }; + use sp_runtime::DispatchError; use sp_trie::{ record_all_keys, trie_types::TrieDBMutV1, LayoutV1, MemoryDB, Recorder, TrieMut, }; @@ -837,4 +887,6 @@ mod tests { .0, ); } + + generate_owned_bridge_module_tests!(BasicOperatingMode::Normal, BasicOperatingMode::Halted); }