diff --git a/Cargo.lock b/Cargo.lock index c571747f15..bcd0b4fd8d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5872,6 +5872,7 @@ dependencies = [ "bitvec", "bp-messages", "bp-runtime", + "bp-test-utils", "frame-benchmarking", "frame-support", "frame-system", diff --git a/modules/grandpa/src/lib.rs b/modules/grandpa/src/lib.rs index 056aee406d..c75ccc2e80 100644 --- a/modules/grandpa/src/lib.rs +++ b/modules/grandpa/src/lib.rs @@ -59,6 +59,9 @@ pub mod benchmarking; pub use pallet::*; pub use weights::WeightInfo; +/// The target that will be used when publishing logs related to this pallet. +const LOG_TARGET: &str = "runtime::bridge-grandpa"; + /// Block number of the bridged chain. pub type BridgedBlockNumber = BlockNumberOf<>::BridgedChain>; /// Block hash of the bridged chain. @@ -119,7 +122,7 @@ pub mod pallet { } impl, I: 'static> OwnedBridgeModule for Pallet { - const LOG_TARGET: &'static str = "runtime::bridge-grandpa"; + const LOG_TARGET: &'static str = LOG_TARGET; type OwnerStorage = PalletOwner; type OperatingMode = BasicOperatingMode; type OperatingModeStorage = PalletOperatingMode; @@ -149,7 +152,11 @@ pub mod pallet { ensure!(Self::request_count() < T::MaxRequests::get(), >::TooManyRequests); let (hash, number) = (finality_target.hash(), finality_target.number()); - log::trace!(target: "runtime::bridge-grandpa", "Going to try and finalize header {:?}", finality_target); + log::trace!( + target: LOG_TARGET, + "Going to try and finalize header {:?}", + finality_target + ); let best_finalized = BestFinalized::::get(); let best_finalized = @@ -158,7 +165,7 @@ pub mod pallet { Some(best_finalized) => best_finalized, None => { log::error!( - target: "runtime::bridge-grandpa", + target: LOG_TARGET, "Cannot finalize header {:?} because pallet is not yet initialized", finality_target, ); @@ -179,7 +186,11 @@ pub mod pallet { try_enact_authority_change::(&finality_target, set_id)?; >::mutate(|count| *count += 1); insert_header::(*finality_target, hash); - log::info!(target: "runtime::bridge-grandpa", "Successfully imported finalized header with hash {:?}!", hash); + log::info!( + target: LOG_TARGET, + "Successfully imported finalized header with hash {:?}!", + hash + ); // mandatory header is a header that changes authorities set. The pallet can't go // further without importing this header. So every bridge MUST import mandatory headers. @@ -213,7 +224,7 @@ pub mod pallet { initialize_bridge::(init_data.clone()); log::info!( - target: "runtime::bridge-grandpa", + target: LOG_TARGET, "Pallet has been initialized with the following parameters: {:?}", init_data ); @@ -392,7 +403,7 @@ pub mod pallet { change_enacted = true; log::info!( - target: "runtime::bridge-grandpa", + target: LOG_TARGET, "Transitioned from authority set {} to {}! New authorities are: {:?}", current_set_id, current_set_id + 1, @@ -429,7 +440,7 @@ pub mod pallet { ) .map_err(|e| { log::error!( - target: "runtime::bridge-grandpa", + target: LOG_TARGET, "Received invalid justification for {:?}: {:?}", hash, e, @@ -455,7 +466,7 @@ pub mod pallet { // Update ring buffer pointer and remove old header. >::put((index + 1) % T::HeadersToKeep::get()); if let Ok(hash) = pruning { - log::debug!(target: "runtime::bridge-grandpa", "Pruning old header: {:?}.", hash); + log::debug!(target: LOG_TARGET, "Pruning old header: {:?}.", hash); >::remove(hash); } } @@ -589,8 +600,8 @@ mod tests { 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, + authority_list, generate_owned_bridge_module_tests, make_default_justification, + make_justification_for_header, JustificationGeneratorParams, ALICE, BOB, }; use codec::Encode; use frame_support::{ @@ -704,103 +715,6 @@ mod tests { }) } - #[test] - fn pallet_owner_may_change_owner() { - run_test(|| { - PalletOwner::::put(2); - - assert_ok!(Pallet::::set_owner(Origin::root(), Some(1))); - assert_noop!( - Pallet::::set_operating_mode( - Origin::signed(2), - BasicOperatingMode::Halted - ), - DispatchError::BadOrigin, - ); - assert_ok!(Pallet::::set_operating_mode( - Origin::root(), - BasicOperatingMode::Halted - )); - - assert_ok!(Pallet::::set_owner(Origin::signed(1), None)); - assert_noop!( - Pallet::::set_operating_mode( - Origin::signed(1), - BasicOperatingMode::Normal - ), - DispatchError::BadOrigin, - ); - assert_noop!( - Pallet::::set_operating_mode( - Origin::signed(2), - BasicOperatingMode::Normal - ), - DispatchError::BadOrigin, - ); - assert_ok!(Pallet::::set_operating_mode( - Origin::root(), - BasicOperatingMode::Normal - )); - }); - } - - #[test] - fn pallet_may_be_halted_by_root() { - run_test(|| { - assert_ok!(Pallet::::set_operating_mode( - Origin::root(), - BasicOperatingMode::Halted - )); - assert_ok!(Pallet::::set_operating_mode( - Origin::root(), - BasicOperatingMode::Normal - )); - }); - } - - #[test] - fn pallet_may_be_halted_by_owner() { - run_test(|| { - PalletOwner::::put(2); - - assert_ok!(Pallet::::set_operating_mode( - Origin::signed(2), - BasicOperatingMode::Halted - )); - assert_ok!(Pallet::::set_operating_mode( - Origin::signed(2), - BasicOperatingMode::Normal - )); - - assert_noop!( - Pallet::::set_operating_mode( - Origin::signed(1), - BasicOperatingMode::Halted - ), - DispatchError::BadOrigin, - ); - assert_noop!( - Pallet::::set_operating_mode( - Origin::signed(1), - BasicOperatingMode::Normal - ), - DispatchError::BadOrigin, - ); - - assert_ok!(Pallet::::set_operating_mode( - Origin::signed(2), - BasicOperatingMode::Halted - )); - assert_noop!( - Pallet::::set_operating_mode( - Origin::signed(1), - BasicOperatingMode::Normal - ), - DispatchError::BadOrigin, - ); - }); - } - #[test] fn pallet_rejects_transactions_if_halted() { run_test(|| { @@ -1173,4 +1087,6 @@ mod tests { bp_header_chain::storage_keys::best_finalized_key("Grandpa").0, ); } + + generate_owned_bridge_module_tests!(BasicOperatingMode::Normal, BasicOperatingMode::Halted); } diff --git a/modules/messages/Cargo.toml b/modules/messages/Cargo.toml index 47d41dba17..5f98523266 100644 --- a/modules/messages/Cargo.toml +++ b/modules/messages/Cargo.toml @@ -31,6 +31,7 @@ sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", d [dev-dependencies] sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" } pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" } +bp-test-utils = { path = "../../primitives/test-utils" } [features] default = ["std"] diff --git a/modules/messages/src/lib.rs b/modules/messages/src/lib.rs index eed718477b..a990101818 100644 --- a/modules/messages/src/lib.rs +++ b/modules/messages/src/lib.rs @@ -90,6 +90,9 @@ mod mock; pub use pallet::*; +/// The target that will be used when publishing logs related to this pallet. +const LOG_TARGET: &str = "runtime::bridge-messages"; + #[frame_support::pallet] pub mod pallet { use super::*; @@ -217,7 +220,7 @@ pub mod pallet { pub struct Pallet(PhantomData<(T, I)>); impl, I: 'static> OwnedBridgeModule for Pallet { - const LOG_TARGET: &'static str = "runtime::bridge-messages"; + const LOG_TARGET: &'static str = LOG_TARGET; type OwnerStorage = PalletOwner; type OperatingMode = MessagesOperatingMode; type OperatingModeStorage = PalletOperatingMode; @@ -309,7 +312,7 @@ pub mod pallet { ) .map_err(|err| { log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Submitter can't pay additional fee {:?} for the message {:?}/{:?} to {:?}: {:?}", additional_fee, lane_id, @@ -389,11 +392,7 @@ pub mod pallet { T::InboundPayload, >(proof, messages_count) .map_err(|err| { - log::trace!( - target: "runtime::bridge-messages", - "Rejecting invalid messages proof: {:?}", - err, - ); + log::trace!(target: LOG_TARGET, "Rejecting invalid messages proof: {:?}", err,); Error::::InvalidMessagesProof })?; @@ -409,7 +408,7 @@ pub mod pallet { let updated_latest_confirmed_nonce = lane.receive_state_update(lane_state); if let Some(updated_latest_confirmed_nonce) = updated_latest_confirmed_nonce { log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Received lane {:?} state update: latest_confirmed_nonce={}", lane_id, updated_latest_confirmed_nonce, @@ -426,7 +425,7 @@ pub mod pallet { let dispatch_weight = T::MessageDispatch::dispatch_weight(&mut message); if dispatch_weight > dispatch_weight_left { log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Cannot dispatch any more messages on lane {:?}. Weight: declared={}, left={}", lane_id, dispatch_weight, @@ -478,7 +477,7 @@ pub mod pallet { } log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Received messages: total={}, valid={}. Weight used: {}/{}", total_messages, valid_messages, @@ -524,7 +523,7 @@ pub mod pallet { let (lane_id, lane_data) = T::TargetHeaderChain::verify_messages_delivery_proof(proof) .map_err(|err| { log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Rejecting invalid messages delivery proof: {:?}", err, ); @@ -564,7 +563,7 @@ pub mod pallet { to_confirm_messages_count, ) => { log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Messages delivery proof contains too many messages to confirm: {} vs declared {}", to_confirm_messages_count, relayers_state.total_messages, @@ -574,7 +573,7 @@ pub mod pallet { }, error => { log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Messages delivery proof contains invalid unrewarded relayers vec: {:?}", error, ); @@ -593,7 +592,7 @@ pub mod pallet { Some(difference) if difference == 0 => (), Some(difference) => { log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "T::OnDeliveryConfirmed callback has spent less weight than expected. Refunding: \ {} - {} = {}", preliminary_callback_overhead, @@ -608,7 +607,7 @@ pub mod pallet { "T::OnDeliveryConfirmed callback consumed too much weight." ); log::error!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "T::OnDeliveryConfirmed callback has spent more weight that it is allowed to: \ {} vs {}", preliminary_callback_overhead, @@ -634,7 +633,7 @@ pub mod pallet { } log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Received messages delivery proof up to (and including) {} at lane {:?}", last_delivered_nonce, lane_id, @@ -829,7 +828,7 @@ fn send_message, I: 'static>( // let's first check if message can be delivered to target chain T::TargetHeaderChain::verify_message(&payload).map_err(|err| { log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Message to lane {:?} is rejected by target chain: {:?}", lane_id, err, @@ -849,7 +848,7 @@ fn send_message, I: 'static>( ) .map_err(|err| { log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Message to lane {:?} is rejected by lane verifier: {:?}", lane_id, err, @@ -866,7 +865,7 @@ fn send_message, I: 'static>( ) .map_err(|err| { log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Message to lane {:?} is rejected because submitter is unable to pay fee {:?}: {:?}", lane_id, delivery_and_dispatch_fee, @@ -892,7 +891,7 @@ fn send_message, I: 'static>( Some(difference) if difference == 0 => (), Some(difference) => { log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "T::OnMessageAccepted callback has spent less weight than expected. Refunding: \ {} - {} = {}", single_message_callback_overhead, @@ -904,7 +903,7 @@ fn send_message, I: 'static>( None => { debug_assert!(false, "T::OnMessageAccepted callback consumed too much weight."); log::error!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "T::OnMessageAccepted callback has spent more weight that it is allowed to: \ {} vs {}", single_message_callback_overhead, @@ -923,7 +922,7 @@ fn send_message, I: 'static>( } log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Accepted message {} to lane {:?}. Message size: {:?}", nonce, lane_id, @@ -1121,6 +1120,7 @@ mod tests { REGULAR_PAYLOAD, TEST_LANE_ID, TEST_RELAYER_A, TEST_RELAYER_B, }; use bp_messages::{UnrewardedRelayer, UnrewardedRelayersState}; + use bp_test_utils::generate_owned_bridge_module_tests; use frame_support::{ assert_noop, assert_ok, storage::generator::{StorageMap, StorageValue}, @@ -1224,103 +1224,6 @@ mod tests { ); } - #[test] - fn pallet_owner_may_change_owner() { - run_test(|| { - PalletOwner::::put(2); - - assert_ok!(Pallet::::set_owner(Origin::root(), Some(1))); - assert_noop!( - Pallet::::set_operating_mode( - Origin::signed(2), - MessagesOperatingMode::Basic(BasicOperatingMode::Halted) - ), - DispatchError::BadOrigin, - ); - assert_ok!(Pallet::::set_operating_mode( - Origin::root(), - MessagesOperatingMode::Basic(BasicOperatingMode::Halted) - )); - - assert_ok!(Pallet::::set_owner(Origin::signed(1), None)); - assert_noop!( - Pallet::::set_operating_mode( - Origin::signed(1), - MessagesOperatingMode::Basic(BasicOperatingMode::Normal) - ), - DispatchError::BadOrigin, - ); - assert_noop!( - Pallet::::set_operating_mode( - Origin::signed(2), - MessagesOperatingMode::Basic(BasicOperatingMode::Normal) - ), - DispatchError::BadOrigin, - ); - assert_ok!(Pallet::::set_operating_mode( - Origin::root(), - MessagesOperatingMode::Basic(BasicOperatingMode::Normal) - )); - }); - } - - #[test] - fn pallet_may_be_halted_by_root() { - run_test(|| { - assert_ok!(Pallet::::set_operating_mode( - Origin::root(), - MessagesOperatingMode::Basic(BasicOperatingMode::Halted) - )); - assert_ok!(Pallet::::set_operating_mode( - Origin::root(), - MessagesOperatingMode::Basic(BasicOperatingMode::Normal) - )); - }); - } - - #[test] - fn pallet_may_be_halted_by_owner() { - run_test(|| { - PalletOwner::::put(2); - - assert_ok!(Pallet::::set_operating_mode( - Origin::signed(2), - MessagesOperatingMode::Basic(BasicOperatingMode::Halted) - )); - assert_ok!(Pallet::::set_operating_mode( - Origin::signed(2), - MessagesOperatingMode::Basic(BasicOperatingMode::Normal) - )); - - assert_noop!( - Pallet::::set_operating_mode( - Origin::signed(1), - MessagesOperatingMode::Basic(BasicOperatingMode::Halted) - ), - DispatchError::BadOrigin, - ); - assert_noop!( - Pallet::::set_operating_mode( - Origin::signed(1), - MessagesOperatingMode::Basic(BasicOperatingMode::Normal) - ), - DispatchError::BadOrigin, - ); - - assert_ok!(Pallet::::set_operating_mode( - Origin::signed(2), - MessagesOperatingMode::Basic(BasicOperatingMode::Halted) - )); - assert_noop!( - Pallet::::set_operating_mode( - Origin::signed(1), - MessagesOperatingMode::Basic(BasicOperatingMode::Normal) - ), - DispatchError::BadOrigin, - ); - }); - } - #[test] fn pallet_parameter_may_be_updated_by_root() { run_test(|| { @@ -2412,4 +2315,9 @@ mod tests { ); }); } + + generate_owned_bridge_module_tests!( + MessagesOperatingMode::Basic(BasicOperatingMode::Normal), + MessagesOperatingMode::Basic(BasicOperatingMode::Halted) + ); } diff --git a/modules/parachains/src/lib.rs b/modules/parachains/src/lib.rs index dc5ad5074b..ec2d16427e 100644 --- a/modules/parachains/src/lib.rs +++ b/modules/parachains/src/lib.rs @@ -48,6 +48,9 @@ mod extension; #[cfg(test)] mod mock; +/// The target that will be used when publishing logs related to this pallet. +const LOG_TARGET: &str = "runtime::bridge-parachains"; + /// Block hash of the bridged relay chain. pub type RelayBlockHash = bp_polkadot_core::Hash; /// Block number of the bridged relay chain. @@ -77,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::*; @@ -97,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] @@ -135,6 +141,23 @@ pub mod pallet { type HeadsToKeep: Get; } + /// 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, 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, I: 'static = ()> = + StorageValue<_, BasicOperatingMode, ValueQuery>; + /// Best parachain heads. #[pallet::storage] pub type BestParaHeads, I: 'static = ()> = @@ -155,6 +178,13 @@ pub mod pallet { #[pallet::without_storage_info] pub struct Pallet(PhantomData<(T, I)>); + impl, I: 'static> OwnedBridgeModule for Pallet { + const LOG_TARGET: &'static str = LOG_TARGET; + type OwnerStorage = PalletOwner; + type OperatingMode = BasicOperatingMode; + type OperatingModeStorage = PalletOperatingMode; + } + #[pallet::call] impl, I: 'static> Pallet where @@ -182,6 +212,7 @@ pub mod pallet { parachains: Vec, parachain_heads_proof: ParaHeadsProof, ) -> DispatchResultWithPostInfo { + Self::ensure_not_halted().map_err(Error::::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::< @@ -208,7 +239,7 @@ pub mod pallet { // if we're not tracking this parachain, we'll just ignore its head proof here if !T::TrackedParachains::contains(¶chain) { log::trace!( - target: "runtime::bridge-parachains", + target: LOG_TARGET, "The head of parachain {:?} has been provided, but it is not tracked by the pallet", parachain, ); @@ -219,7 +250,7 @@ pub mod pallet { Ok(Some(parachain_head)) => parachain_head, Ok(None) => { log::trace!( - target: "runtime::bridge-parachains", + target: LOG_TARGET, "The head of parachain {:?} is None. {}", parachain, if BestParaHeads::::contains_key(¶chain) { @@ -232,7 +263,7 @@ pub mod pallet { }, Err(e) => { log::trace!( - target: "runtime::bridge-parachains", + target: LOG_TARGET, "The read of head of parachain {:?} has failed: {:?}", parachain, e, @@ -263,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, new_owner: Option) -> DispatchResult { + >::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, + operating_mode: BasicOperatingMode, + ) -> DispatchResult { + >::set_operating_mode(origin, operating_mode) + } } impl, I: 'static> Pallet { @@ -325,7 +375,7 @@ pub mod pallet { // check if this head has already been imported before if updated_head_hash == stored_best_head.head_hash { log::trace!( - target: "runtime::bridge-parachains", + target: LOG_TARGET, "The head of parachain {:?} can't be updated to {}, because it has been already updated\ to the same value at previous relay chain block: {} < {}", parachain, @@ -341,7 +391,7 @@ pub mod pallet { None => 0, Some(stored_best_head) => { log::trace!( - target: "runtime::bridge-parachains", + target: LOG_TARGET, "The head of parachain {:?} can't be updated to {}, because it has been already updated\ to {} at better relay chain block: {} > {}", parachain, @@ -370,7 +420,7 @@ pub mod pallet { ); ImportedParaHeads::::insert(parachain, updated_head_hash, updated_head); log::trace!( - target: "runtime::bridge-parachains", + target: LOG_TARGET, "Updated head of parachain {:?} to {}", parachain, updated_head_hash, @@ -380,7 +430,7 @@ pub mod pallet { let prune_happened = head_hash_to_prune.is_ok(); if let Ok(head_hash_to_prune) = head_hash_to_prune { log::trace!( - target: "runtime::bridge-parachains", + target: LOG_TARGET, "Pruning old head of parachain {:?}: {}", parachain, head_hash_to_prune, @@ -391,6 +441,37 @@ pub mod pallet { Ok(UpdateParachainHeadArtifacts { best_head: updated_best_para_head, prune_happened }) } } + + #[pallet::genesis_config] + pub struct GenesisConfig, I: 'static = ()> { + /// Initial pallet operating mode. + pub operating_mode: BasicOperatingMode, + /// Initial pallet owner. + pub owner: Option, + /// Dummy marker. + pub phantom: sp_std::marker::PhantomData, + } + + #[cfg(feature = "std")] + impl, I: 'static> Default for GenesisConfig { + fn default() -> Self { + Self { + operating_mode: Default::default(), + owner: Default::default(), + phantom: Default::default(), + } + } + } + + #[pallet::genesis_build] + impl, I: 'static> GenesisBuild for GenesisConfig { + fn build(&self) { + PalletOperatingMode::::put(&self.operating_mode); + if let Some(ref owner) = self.owner { + PalletOwner::::put(owner); + } + } + } } #[cfg(test)] @@ -400,8 +481,10 @@ 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 bp_runtime::{BasicOperatingMode, OwnedBridgeModuleError}; + use bp_test_utils::{ + authority_list, generate_owned_bridge_module_tests, make_default_justification, + }; use frame_support::{ assert_noop, assert_ok, dispatch::DispatchResultWithPostInfo, @@ -409,6 +492,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, }; @@ -509,6 +593,36 @@ mod tests { }) } + #[test] + fn submit_parachain_heads_checks_operating_mode() { + let (state_root, proof) = prepare_parachain_heads_proof(vec![(1, head_data(1, 0))]); + + run_test(|| { + initialize(state_root); + + // `submit_parachain_heads()` should fail when the pallet is halted. + PalletOperatingMode::::put(BasicOperatingMode::Halted); + assert_noop!( + Pallet::::submit_parachain_heads( + Origin::signed(1), + (0, test_relay_header(0, state_root).hash()), + vec![ParaId(1), ParaId(2), ParaId(3)], + proof.clone(), + ), + Error::::BridgeModule(OwnedBridgeModuleError::Halted) + ); + + // `submit_parachain_heads()` should succeed now that the pallet is resumed. + PalletOperatingMode::::put(BasicOperatingMode::Normal); + assert_ok!(Pallet::::submit_parachain_heads( + Origin::signed(1), + (0, test_relay_header(0, state_root).hash()), + vec![ParaId(1)], + proof, + ),); + }); + } + #[test] fn imports_initial_parachain_heads() { let (state_root, proof) = @@ -834,4 +948,6 @@ mod tests { .0, ); } + + generate_owned_bridge_module_tests!(BasicOperatingMode::Normal, BasicOperatingMode::Halted); } diff --git a/primitives/test-utils/src/lib.rs b/primitives/test-utils/src/lib.rs index 38d9453c98..66dbe9e738 100644 --- a/primitives/test-utils/src/lib.rs +++ b/primitives/test-utils/src/lib.rs @@ -210,3 +210,92 @@ pub fn test_header(number: H::Number) -> H { pub fn header_id(index: u8) -> (H::Hash, H::Number) { (test_header::(index.into()).hash(), index.into()) } + +#[macro_export] +/// Adds methods for testing the `set_owner()` and `set_operating_mode()` for a pallet. +/// Some values are hardcoded like: +/// - `run_test()` +/// - `Pallet::` +/// - `PalletOwner::` +/// - `PalletOperatingMode::` +/// While this is not ideal, all the pallets use the same names, so it works for the moment. +/// We can revisit this in the future if anything changes. +macro_rules! generate_owned_bridge_module_tests { + ($normal_operating_mode: expr, $halted_operating_mode: expr) => { + #[test] + fn test_set_owner() { + run_test(|| { + PalletOwner::::put(1); + + // The root should be able to change the owner. + assert_ok!(Pallet::::set_owner(Origin::root(), Some(2))); + assert_eq!(PalletOwner::::get(), Some(2)); + + // The owner should be able to change the owner. + assert_ok!(Pallet::::set_owner(Origin::signed(2), Some(3))); + assert_eq!(PalletOwner::::get(), Some(3)); + + // Other users shouldn't be able to change the owner. + assert_noop!( + Pallet::::set_owner(Origin::signed(1), Some(4)), + DispatchError::BadOrigin + ); + assert_eq!(PalletOwner::::get(), Some(3)); + }); + } + + #[test] + fn test_set_operating_mode() { + run_test(|| { + PalletOwner::::put(1); + PalletOperatingMode::::put($normal_operating_mode); + + // The root should be able to halt the pallet. + assert_ok!(Pallet::::set_operating_mode( + Origin::root(), + $halted_operating_mode + )); + assert_eq!(PalletOperatingMode::::get(), $halted_operating_mode); + // The root should be able to resume the pallet. + assert_ok!(Pallet::::set_operating_mode( + Origin::root(), + $normal_operating_mode + )); + assert_eq!(PalletOperatingMode::::get(), $normal_operating_mode); + + // The owner should be able to halt the pallet. + assert_ok!(Pallet::::set_operating_mode( + Origin::signed(1), + $halted_operating_mode + )); + assert_eq!(PalletOperatingMode::::get(), $halted_operating_mode); + // The owner should be able to resume the pallet. + assert_ok!(Pallet::::set_operating_mode( + Origin::signed(1), + $normal_operating_mode + )); + assert_eq!(PalletOperatingMode::::get(), $normal_operating_mode); + + // Other users shouldn't be able to halt the pallet. + assert_noop!( + Pallet::::set_operating_mode( + Origin::signed(2), + $halted_operating_mode + ), + DispatchError::BadOrigin + ); + assert_eq!(PalletOperatingMode::::get(), $normal_operating_mode); + // Other users shouldn't be able to resume the pallet. + PalletOperatingMode::::put($halted_operating_mode); + assert_noop!( + Pallet::::set_operating_mode( + Origin::signed(2), + $normal_operating_mode + ), + DispatchError::BadOrigin + ); + assert_eq!(PalletOperatingMode::::get(), $halted_operating_mode); + }); + } + }; +}