diff --git a/bridges/modules/grandpa/src/lib.rs b/bridges/modules/grandpa/src/lib.rs index 1527de80fc22ba94caaee3dbb5ee0676b15e565c..726de9c507ed4a1cd716b282cb062e5474bffa26 100644 --- a/bridges/modules/grandpa/src/lib.rs +++ b/bridges/modules/grandpa/src/lib.rs @@ -37,12 +37,12 @@ #![allow(clippy::large_enum_variant)] use bp_header_chain::{justification::GrandpaJustification, InitializationData}; -use bp_runtime::{BlockNumberOf, Chain, HashOf, HasherOf, HeaderOf}; +use bp_runtime::{BlockNumberOf, Chain, HashOf, HasherOf, HeaderOf, OwnedBridgeModule}; use finality_grandpa::voter_set::VoterSet; use frame_support::{ensure, fail}; -use frame_system::{ensure_signed, RawOrigin}; +use frame_system::ensure_signed; use sp_finality_grandpa::{ConsensusLog, GRANDPA_ENGINE_ID}; -use sp_runtime::traits::{BadOrigin, Header as HeaderT, Zero}; +use sp_runtime::traits::{Header as HeaderT, Zero}; use sp_std::{boxed::Box, convert::TryInto}; mod extension; @@ -117,6 +117,18 @@ 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() + } + } + #[pallet::call] impl<T: Config<I>, I: 'static> Pallet<T, I> { /// Verify a target header is finalized according to the given finality proof. @@ -135,7 +147,7 @@ pub mod pallet { finality_target: Box<BridgedHeader<T, I>>, justification: GrandpaJustification<BridgedHeader<T, I>>, ) -> DispatchResultWithPostInfo { - ensure_operational::<T, I>()?; + Self::ensure_not_halted().map_err(Error::<T, I>::BridgeModule)?; let _ = ensure_signed(origin)?; ensure!(Self::request_count() < T::MaxRequests::get(), <Error<T, I>>::TooManyRequests); @@ -198,7 +210,7 @@ pub mod pallet { origin: OriginFor<T>, init_data: super::InitializationData<BridgedHeader<T, I>>, ) -> DispatchResultWithPostInfo { - ensure_owner_or_root::<T, I>(origin)?; + Self::ensure_owner_or_root(origin)?; let init_allowed = !<BestFinalized<T, I>>::exists(); ensure!(init_allowed, <Error<T, I>>::AlreadyInitialized); @@ -217,43 +229,16 @@ 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_owner( - origin: OriginFor<T>, - new_owner: Option<T::AccountId>, - ) -> DispatchResultWithPostInfo { - ensure_owner_or_root::<T, I>(origin)?; - match new_owner { - Some(new_owner) => { - PalletOwner::<T, I>::put(&new_owner); - log::info!(target: "runtime::bridge-grandpa", "Setting pallet Owner to: {:?}", new_owner); - }, - None => { - PalletOwner::<T, I>::kill(); - log::info!(target: "runtime::bridge-grandpa", "Removed Owner of pallet."); - }, - } - - Ok(().into()) + 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_operational( - origin: OriginFor<T>, - operational: bool, - ) -> DispatchResultWithPostInfo { - ensure_owner_or_root::<T, I>(origin)?; - <IsHalted<T, I>>::put(!operational); - - if operational { - log::info!(target: "runtime::bridge-grandpa", "Resuming pallet operations."); - } else { - log::warn!(target: "runtime::bridge-grandpa", "Stopping pallet operations."); - } - - Ok(().into()) + pub fn set_operational(origin: OriginFor<T>, operational: bool) -> DispatchResult { + Self::set_operating_mode(origin, !operational) } } @@ -310,7 +295,7 @@ pub mod pallet { /// If true, all pallet transactions are failed immediately. #[pallet::storage] - pub(super) type IsHalted<T: Config<I>, I: 'static = ()> = StorageValue<_, bool, ValueQuery>; + pub type IsHalted<T: Config<I>, I: 'static = ()> = StorageValue<_, bool, ValueQuery>; #[pallet::genesis_config] pub struct GenesisConfig<T: Config<I>, I: 'static = ()> { @@ -364,10 +349,10 @@ pub mod pallet { NotInitialized, /// The pallet has already been initialized. AlreadyInitialized, - /// All pallet operations are halted. - Halted, /// The storage proof doesn't contains storage root. So it is invalid for given header. StorageRootMismatch, + /// Error generated by the `OwnedBridgeModule` trait. + BridgeModule(bp_runtime::OwnedBridgeModuleError), } /// Check the given header for a GRANDPA scheduled authority set change. If a change @@ -513,26 +498,6 @@ pub mod pallet { insert_header::<T, I>(header, hash); } } - - /// Ensure that the origin is either root, or `PalletOwner`. - fn ensure_owner_or_root<T: Config<I>, I: 'static>(origin: T::Origin) -> Result<(), BadOrigin> { - match origin.into() { - Ok(RawOrigin::Root) => Ok(()), - Ok(RawOrigin::Signed(ref signer)) - if Some(signer) == <PalletOwner<T, I>>::get().as_ref() => - Ok(()), - _ => Err(BadOrigin), - } - } - - /// Ensure that the pallet is in operational mode (not halted). - fn ensure_operational<T: Config<I>, I: 'static>() -> Result<(), Error<T, I>> { - if <IsHalted<T, I>>::get() { - Err(<Error<T, I>>::Halted) - } else { - Ok(()) - } - } } impl<T: Config<I>, I: 'static> Pallet<T, I> { @@ -799,7 +764,10 @@ mod tests { initialize_substrate_bridge(); assert_ok!(Pallet::<TestRuntime>::set_operational(Origin::root(), false)); - assert_noop!(submit_finality_proof(1), Error::<TestRuntime>::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!(submit_finality_proof(1)); diff --git a/bridges/modules/messages/src/lib.rs b/bridges/modules/messages/src/lib.rs index 0f287a9df47e1f026ba0a180350caf31234982f3..78f44e75027b58025181fb81f967e6da97feaced 100644 --- a/bridges/modules/messages/src/lib.rs +++ b/bridges/modules/messages/src/lib.rs @@ -61,17 +61,16 @@ use bp_messages::{ OutboundMessageDetails, Parameter as MessagesParameter, UnrewardedRelayer, UnrewardedRelayersState, }; -use bp_runtime::{ChainId, Size}; +use bp_runtime::{ChainId, OwnedBridgeModule, Size}; use codec::{Decode, Encode}; use frame_support::{ fail, traits::Get, weights::{Pays, PostDispatchInfo}, }; -use frame_system::RawOrigin; use num_traits::{SaturatingAdd, Zero}; use sp_core::H256; -use sp_runtime::traits::{BadOrigin, Convert}; +use sp_runtime::traits::Convert; use sp_std::{ cell::RefCell, cmp::PartialOrd, collections::vec_deque::VecDeque, marker::PhantomData, ops::RangeInclusive, prelude::*, @@ -217,6 +216,18 @@ 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 = "runtime::bridge-messages"; + const OPERATING_MODE_KEY: &'static str = "PalletOperatingMode"; + type OwnerStorage = PalletOwner<T, I>; + type OperatingMode = OperatingMode; + type OperatingModeStorage = PalletOperatingMode<T, I>; + + fn is_halted() -> bool { + Self::OperatingModeStorage::get() == OperatingMode::Halted + } + } + #[pallet::call] impl<T: Config<I>, I: 'static> Pallet<T, I> { /// Change `PalletOwner`. @@ -224,18 +235,7 @@ 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_owner(origin: OriginFor<T>, new_owner: Option<T::AccountId>) -> DispatchResult { - ensure_owner_or_root::<T, I>(origin)?; - match new_owner { - Some(new_owner) => { - PalletOwner::<T, I>::put(&new_owner); - log::info!(target: "runtime::bridge-messages", "Setting pallet Owner to: {:?}", new_owner); - }, - None => { - PalletOwner::<T, I>::kill(); - log::info!(target: "runtime::bridge-messages", "Removed Owner of pallet."); - }, - } - Ok(()) + <Self as OwnedBridgeModule<_>>::set_owner(origin, new_owner) } /// Halt or resume all/some pallet operations. @@ -246,14 +246,7 @@ pub mod pallet { origin: OriginFor<T>, operating_mode: OperatingMode, ) -> DispatchResult { - ensure_owner_or_root::<T, I>(origin)?; - PalletOperatingMode::<T, I>::put(operating_mode); - log::info!( - target: "runtime::bridge-messages", - "Setting messages pallet operating mode to {:?}.", - operating_mode, - ); - Ok(()) + <Self as OwnedBridgeModule<_>>::set_operating_mode(origin, operating_mode) } /// Update pallet parameter. @@ -267,7 +260,7 @@ pub mod pallet { origin: OriginFor<T>, parameter: T::Parameter, ) -> DispatchResult { - ensure_owner_or_root::<T, I>(origin)?; + Self::ensure_owner_or_root(origin)?; parameter.save(); Self::deposit_event(Event::ParameterUpdated(parameter)); Ok(()) @@ -297,7 +290,7 @@ pub mod pallet { nonce: MessageNonce, additional_fee: T::OutboundMessageFee, ) -> DispatchResultWithPostInfo { - ensure_not_halted::<T, I>()?; + Self::ensure_not_halted().map_err(Error::<T, I>::BridgeModule)?; // if someone tries to pay for already-delivered message, we're rejecting this intention // (otherwise this additional fee will be locked forever in relayers fund) // @@ -368,7 +361,7 @@ pub mod pallet { messages_count: u32, dispatch_weight: Weight, ) -> DispatchResultWithPostInfo { - ensure_not_halted::<T, I>()?; + Self::ensure_not_halted().map_err(Error::<T, I>::BridgeModule)?; let relayer_id_at_this_chain = ensure_signed(origin)?; // reject transactions that are declaring too many messages @@ -512,7 +505,7 @@ pub mod pallet { proof: MessagesDeliveryProofOf<T, I>, relayers_state: UnrewardedRelayersState, ) -> DispatchResultWithPostInfo { - ensure_not_halted::<T, I>()?; + Self::ensure_not_halted().map_err(Error::<T, I>::BridgeModule)?; // why do we need to know the weight of this (`receive_messages_delivery_proof`) call? // Because we may want to return some funds for messages that are not processed by the @@ -669,8 +662,8 @@ pub mod pallet { #[pallet::error] pub enum Error<T, I = ()> { - /// All pallet operations are halted. - Halted, + /// Pallet is not in Normal operating mode. + NotOperatingNormally, /// Message has been treated as invalid by chain verifier. MessageRejectedByChainVerifier, /// Message has been treated as invalid by lane verifier. @@ -695,6 +688,8 @@ pub mod pallet { /// The number of actually confirmed messages is going to be larger than the number of /// messages in the proof. This may mean that this or bridged chain storage is corrupted. TryingToConfirmMoreMessagesThanExpected, + /// Error generated by the `OwnedBridgeModule` trait. + BridgeModule(bp_runtime::OwnedBridgeModuleError), } /// Optional pallet owner. @@ -975,30 +970,10 @@ where relayers_rewards } -/// Ensure that the origin is either root, or `PalletOwner`. -fn ensure_owner_or_root<T: Config<I>, I: 'static>(origin: T::Origin) -> Result<(), BadOrigin> { - match origin.into() { - Ok(RawOrigin::Root) => Ok(()), - Ok(RawOrigin::Signed(ref signer)) - if Some(signer) == Pallet::<T, I>::module_owner().as_ref() => - Ok(()), - _ => Err(BadOrigin), - } -} - /// 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>::Halted) - } else { - Ok(()) - } -} - -/// Ensure that the pallet is not halted. -fn ensure_not_halted<T: Config<I>, I: 'static>() -> Result<(), Error<T, I>> { - if PalletOperatingMode::<T, I>::get() == OperatingMode::Halted { - Err(Error::<T, I>::Halted) + Err(Error::<T, I>::NotOperatingNormally) } else { Ok(()) } @@ -1442,12 +1417,12 @@ mod tests { REGULAR_PAYLOAD, REGULAR_PAYLOAD.declared_weight, ), - Error::<TestRuntime, ()>::Halted, + Error::<TestRuntime, ()>::NotOperatingNormally, ); assert_noop!( Pallet::<TestRuntime>::increase_message_fee(Origin::signed(1), TEST_LANE_ID, 1, 1,), - Error::<TestRuntime, ()>::Halted, + Error::<TestRuntime, ()>::BridgeModule(bp_runtime::OwnedBridgeModuleError::Halted), ); assert_noop!( @@ -1458,7 +1433,7 @@ mod tests { 1, REGULAR_PAYLOAD.declared_weight, ), - Error::<TestRuntime, ()>::Halted, + Error::<TestRuntime, ()>::BridgeModule(bp_runtime::OwnedBridgeModuleError::Halted), ); assert_noop!( @@ -1480,7 +1455,7 @@ mod tests { last_delivered_nonce: 1, }, ), - Error::<TestRuntime, ()>::Halted, + Error::<TestRuntime, ()>::BridgeModule(bp_runtime::OwnedBridgeModuleError::Halted), ); }); } @@ -1500,7 +1475,7 @@ mod tests { REGULAR_PAYLOAD, REGULAR_PAYLOAD.declared_weight, ), - Error::<TestRuntime, ()>::Halted, + Error::<TestRuntime, ()>::NotOperatingNormally, ); assert_ok!(Pallet::<TestRuntime>::increase_message_fee( diff --git a/bridges/primitives/runtime/Cargo.toml b/bridges/primitives/runtime/Cargo.toml index 20208fd5fa0ba1b02f35c758be0e76a4a96fee60..691426aaf04a7ee71bb9ef542b91bc7c921c05ca 100644 --- a/bridges/primitives/runtime/Cargo.toml +++ b/bridges/primitives/runtime/Cargo.toml @@ -15,6 +15,7 @@ scale-info = { version = "2.1.1", default-features = false, features = ["derive" # Substrate Dependencies frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -30,6 +31,7 @@ default = ["std"] std = [ "codec/std", "frame-support/std", + "frame-system/std", "hash-db/std", "num-traits/std", "scale-info/std", diff --git a/bridges/primitives/runtime/src/lib.rs b/bridges/primitives/runtime/src/lib.rs index bd09a651c72048e47b6052827b6b8f4b2d8e9686..ee5e6b5d24e99532186b65b286bdc95d4c80891b 100644 --- a/bridges/primitives/runtime/src/lib.rs +++ b/bridges/primitives/runtime/src/lib.rs @@ -18,11 +18,16 @@ #![cfg_attr(not(feature = "std"), no_std)] -use codec::Encode; -use frame_support::{RuntimeDebug, StorageHasher}; +use codec::{Decode, Encode, FullCodec}; +use frame_support::{ + log, pallet_prelude::DispatchResult, PalletError, RuntimeDebug, StorageHasher, StorageValue, +}; +use frame_system::RawOrigin; +use scale_info::TypeInfo; use sp_core::{hash::H256, storage::StorageKey}; use sp_io::hashing::blake2_256; -use sp_std::{convert::TryFrom, vec, vec::Vec}; +use sp_runtime::traits::BadOrigin; +use sp_std::{convert::TryFrom, fmt::Debug, vec, vec::Vec}; pub use chain::{ AccountIdOf, AccountPublicOf, BalanceOf, BlockNumberOf, Chain, EncodedOrDecodedCall, HashOf, @@ -257,6 +262,79 @@ pub fn storage_value_key(pallet_prefix: &str, value_name: &str) -> StorageKey { StorageKey(final_key) } +/// Error generated by the `OwnedBridgeModule` trait. +#[derive(Encode, Decode, TypeInfo, PalletError)] +pub enum OwnedBridgeModuleError { + /// All pallet operations are halted. + 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>; + + /// Check if the module is halted. + fn is_halted() -> bool; + + /// Ensure that the origin is either root, or `PalletOwner`. + fn ensure_owner_or_root(origin: T::Origin) -> Result<(), BadOrigin> { + match origin.into() { + Ok(RawOrigin::Root) => Ok(()), + Ok(RawOrigin::Signed(ref signer)) + if Self::OwnerStorage::get().as_ref() == Some(signer) => + Ok(()), + _ => Err(BadOrigin), + } + } + + /// Ensure that the module is not halted. + fn ensure_not_halted() -> Result<(), OwnedBridgeModuleError> { + match Self::is_halted() { + true => Err(OwnedBridgeModuleError::Halted), + false => Ok(()), + } + } + + /// Change the owner of the module. + fn set_owner(origin: T::Origin, maybe_owner: Option<T::AccountId>) -> DispatchResult { + Self::ensure_owner_or_root(origin)?; + match maybe_owner { + Some(owner) => { + Self::OwnerStorage::put(&owner); + log::info!(target: Self::LOG_TARGET, "Setting pallet Owner to: {:?}", owner); + }, + None => { + Self::OwnerStorage::kill(); + log::info!(target: Self::LOG_TARGET, "Removed Owner of pallet."); + }, + } + + Ok(()) + } + + /// Halt or resume all/some module operations. + fn set_operating_mode( + origin: T::Origin, + operating_mode: Self::OperatingMode, + ) -> 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 + ); + Ok(()) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/bridges/relays/lib-substrate-relay/src/lib.rs b/bridges/relays/lib-substrate-relay/src/lib.rs index 51edf857838e96e4e6b2b8e5e72704a46af056fb..1af4f67a7caf50cec83eabd1d5a232d7ccf849b1 100644 --- a/bridges/relays/lib-substrate-relay/src/lib.rs +++ b/bridges/relays/lib-substrate-relay/src/lib.rs @@ -97,12 +97,15 @@ impl<AccountId> TaggedAccount<AccountId> { pub fn tag(&self) -> String { match *self { TaggedAccount::Headers { ref bridged_chain, .. } => format!("{}Headers", bridged_chain), - TaggedAccount::Parachains { ref bridged_chain, .. } => - format!("{}Parachains", bridged_chain), - TaggedAccount::Messages { ref bridged_chain, .. } => - format!("{}Messages", bridged_chain), - TaggedAccount::MessagesPalletOwner { ref bridged_chain, .. } => - format!("{}MessagesPalletOwner", bridged_chain), + TaggedAccount::Parachains { ref bridged_chain, .. } => { + format!("{}Parachains", bridged_chain) + }, + TaggedAccount::Messages { ref bridged_chain, .. } => { + format!("{}Messages", bridged_chain) + }, + TaggedAccount::MessagesPalletOwner { ref bridged_chain, .. } => { + format!("{}MessagesPalletOwner", bridged_chain) + }, } } }