From 90db231ea89592cb04d5cc93f05168a9e7560d70 Mon Sep 17 00:00:00 2001 From: elfedy Date: Mon, 18 Mar 2024 13:32:59 -0300 Subject: [PATCH] Some initial tests --- pallets/emergency-para-xcm/Cargo.toml | 3 + pallets/emergency-para-xcm/src/lib.rs | 11 +- pallets/emergency-para-xcm/src/mock.rs | 195 ++++++++++++++++++++++++ pallets/emergency-para-xcm/src/tests.rs | 76 +++++++++ 4 files changed, 283 insertions(+), 2 deletions(-) create mode 100644 pallets/emergency-para-xcm/src/mock.rs create mode 100644 pallets/emergency-para-xcm/src/tests.rs diff --git a/pallets/emergency-para-xcm/Cargo.toml b/pallets/emergency-para-xcm/Cargo.toml index 293e3ea..6594d68 100644 --- a/pallets/emergency-para-xcm/Cargo.toml +++ b/pallets/emergency-para-xcm/Cargo.toml @@ -37,9 +37,12 @@ sp-io = { workspace = true, features = [ "std" ] } default = [ "std" ] std = [ "cumulus-primitives-core/std", + "cumulus-pallet-parachain-system/std", "frame-support/std", "frame-system/std", + "log/std", "pallet-message-queue/std", + "parity-scale-codec/std", "scale-info/std", "sp-runtime/std", "sp-std/std", diff --git a/pallets/emergency-para-xcm/src/lib.rs b/pallets/emergency-para-xcm/src/lib.rs index f939a9c..a40d16b 100644 --- a/pallets/emergency-para-xcm/src/lib.rs +++ b/pallets/emergency-para-xcm/src/lib.rs @@ -19,6 +19,11 @@ #![allow(non_camel_case_types)] #![cfg_attr(not(feature = "std"), no_std)] +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; + pub use pallet::*; use cumulus_pallet_parachain_system::CheckAssociatedRelayNumber; @@ -58,10 +63,12 @@ pub mod pallet { /// Overarching event type type RuntimeEvent: From + IsType<::RuntimeEvent>; - /// TODO doc + /// Used before check associated relay block number. It should be what would be passed to + /// `cumulus_pallet_parachain_system` if this pallet was not being used. type CheckAssociatedRelayNumber: CheckAssociatedRelayNumber; - /// TODO doc + /// Used to check wether message queue is paused when `XcmMode` is `Normal`. It should be + /// what would be passed to `pallet_message_queue` if this pallet was not being used. type QueuePausedQuery: QueuePausedQuery<::Origin>; /// The HRMP handler to be used in normal operating mode diff --git a/pallets/emergency-para-xcm/src/mock.rs b/pallets/emergency-para-xcm/src/mock.rs new file mode 100644 index 0000000..8e23485 --- /dev/null +++ b/pallets/emergency-para-xcm/src/mock.rs @@ -0,0 +1,195 @@ +// Copyright Moonsong Labs +// This file is part of Moonkit. + +// Moonkit is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Moonkit is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Moonkit. If not, see . + +use super::*; +use crate as pallet_emergency_para_xcm; +use cumulus_pallet_parachain_system::{CheckAssociatedRelayNumber, ParachainSetCode}; +use cumulus_primitives_core::{ + relay_chain::BlockNumber as RelayBlockNumber, AggregateMessageOrigin, InboundDownwardMessage, + InboundHrmpMessage, ParaId, PersistedValidationData, +}; +use frame_support::parameter_types; +use frame_support::traits::{ConstU32, ProcessMessage, ProcessMessageError}; +use frame_support::weights::{RuntimeDbWeight, Weight, WeightMeter}; +use frame_system::EnsureRoot; +use sp_core::H256; +use sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, +}; +use std::cell::RefCell; + +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Test { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + ParachainSystem: cumulus_pallet_parachain_system::{Pallet, Call, Storage, Inherent, Event, Config}, + MessageQueue: pallet_message_queue::{Pallet, Call, Storage, Event}, + EmergencyParaXcm: pallet_emergency_para_xcm::{Pallet, Call, Storage, Event}, + } +); + +parameter_types! { + pub const BlockHashCount: u64 = 250; +} + +pub type AccountId = u64; + +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Block = Block; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = ParachainSetCode; + type MaxConsumers = ConstU32<16>; + type RuntimeTask = (); +} + +parameter_types! { + pub ParachainId: ParaId = 100.into(); + pub const RelayOrigin: AggregateMessageOrigin = AggregateMessageOrigin::Parent; +} + +impl cumulus_pallet_parachain_system::Config for Test { + type RuntimeEvent = RuntimeEvent; + type SelfParaId = ParachainId; + type OnSystemEvent = (); + type OutboundXcmpMessageSource = (); + type XcmpMessageHandler = EmergencyParaXcm; + type ReservedXcmpWeight = (); + type DmpQueue = frame_support::traits::EnqueueWithOrigin; + type ReservedDmpWeight = (); + type CheckAssociatedRelayNumber = EmergencyParaXcm; + type WeightInfo = (); +} + +parameter_types! { + pub const MessageQueueMaxStale: u32 = 8; + pub const MessageQueueHeapSize: u32 = 64 * 1024; + pub const MaxWeight: Weight = Weight::MAX; +} + +impl pallet_message_queue::Config for Test { + type RuntimeEvent = RuntimeEvent; + type MessageProcessor = SaveIntoThreadLocal; + type Size = u32; + type HeapSize = MessageQueueHeapSize; + type MaxStale = MessageQueueMaxStale; + type ServiceWeight = MaxWeight; + type QueueChangeHandler = (); + type QueuePausedQuery = EmergencyParaXcm; + type WeightInfo = (); +} + +pub(crate) const PAUSED_THRESHOLD: u32 = 5; + +impl Config for Test { + type RuntimeEvent = RuntimeEvent; + type CheckAssociatedRelayNumber = cumulus_pallet_parachain_system::AnyRelayNumber; + type QueuePausedQuery = (); + type HrmpMessageHandler = (); + type PausedThreshold = ConstU32; + type FastAuthorizeUpgradeOrigin = EnsureRoot; + type PausedToNormalOrigin = EnsureRoot; +} + +// A `MessageProcessor` that stores all messages in thread-local. +// taken from https://github.com/paritytech/polkadot-sdk/blob/3c6ebd9e9bfda58f199cba6ec3023e0d12d6b506/cumulus/pallets/parachain-system/src/mock.rs#L132 +pub struct SaveIntoThreadLocal; + +std::thread_local! { + pub static HANDLED_DMP_MESSAGES: RefCell>> = RefCell::new(Vec::new()); + pub static HANDLED_XCMP_MESSAGES: RefCell)>> = RefCell::new(Vec::new()); +} + +impl ProcessMessage for SaveIntoThreadLocal { + type Origin = AggregateMessageOrigin; + + fn process_message( + message: &[u8], + origin: Self::Origin, + _meter: &mut WeightMeter, + _id: &mut [u8; 32], + ) -> Result { + assert_eq!(origin, Self::Origin::Parent); + + HANDLED_DMP_MESSAGES.with(|m| { + m.borrow_mut().push(message.to_vec()); + Weight::zero() + }); + Ok(true) + } +} + +impl XcmpMessageHandler for SaveIntoThreadLocal { + fn handle_xcmp_messages<'a, I: Iterator>( + iter: I, + _max_weight: Weight, + ) -> Weight { + HANDLED_XCMP_MESSAGES.with(|m| { + for (sender, sent_at, message) in iter { + m.borrow_mut().push((sender, sent_at, message.to_vec())); + } + Weight::zero() + }) + } +} + +pub(crate) struct ExtBuilder; + +impl ExtBuilder { + pub(crate) fn build(self) -> sp_io::TestExternalities { + let mut storage = frame_system::GenesisConfig::::default() + .build_storage() + .expect("Frame system builds valid default genesis config"); + + let mut ext = sp_io::TestExternalities::new(storage); + ext.execute_with(|| System::set_block_number(1)); + ext + } +} + +pub(crate) fn events() -> Vec { + System::events() + .into_iter() + .map(|r| r.event) + .filter_map(|e| { + if let RuntimeEvent::EmergencyParaXcm(inner) = e { + Some(inner) + } else { + None + } + }) + .collect::>() +} diff --git a/pallets/emergency-para-xcm/src/tests.rs b/pallets/emergency-para-xcm/src/tests.rs new file mode 100644 index 0000000..652abdc --- /dev/null +++ b/pallets/emergency-para-xcm/src/tests.rs @@ -0,0 +1,76 @@ +// Copyright Moonsong Labs +// This file is part of Moonkit. + +// Moonkit is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Moonkit is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Moonkit. If not, see . +use crate::mock::*; +use crate::*; +use frame_support::{assert_noop, assert_ok}; +use sp_runtime::traits::Dispatchable; + +#[test] +fn remains_in_normal_mode_when_relay_block_diff_is_within_threshold() { + ExtBuilder {}.build().execute_with(|| { + let current_block = 1; + let new_block = PAUSED_THRESHOLD + current_block; + EmergencyParaXcm::check_associated_relay_number(new_block, current_block); + let xcm_mode = EmergencyParaXcm::mode(); + assert!(xcm_mode == XcmMode::Normal); + }) +} + +#[test] +fn pauses_xcm_when_relay_block_diff_is_above_threshold() { + ExtBuilder {}.build().execute_with(|| { + let current_block = 1; + let new_block = PAUSED_THRESHOLD + current_block + 1; + EmergencyParaXcm::check_associated_relay_number(new_block, current_block); + let xcm_mode = EmergencyParaXcm::mode(); + assert!(xcm_mode == XcmMode::Paused); + assert_eq!(events(), vec![Event::EnteredPausedXcmMode,]); + }) +} + +#[test] +fn cannot_go_from_paused_to_normal_with_wrong_origin() { + ExtBuilder {}.build().execute_with(|| { + Mode::::set(XcmMode::Paused); + let call: RuntimeCall = Call::paused_to_normal {}.into(); + assert_noop!( + call.dispatch(RuntimeOrigin::signed(1)), + sp_runtime::DispatchError::BadOrigin + ); + }); +} + +#[test] +fn cannot_go_from_paused_to_normal_when_not_paused() { + ExtBuilder {}.build().execute_with(|| { + let call: RuntimeCall = Call::paused_to_normal {}.into(); + assert_noop!( + call.dispatch(RuntimeOrigin::root()), + Error::::NotInPausedMode + ); + }); +} + +#[test] +fn can_go_from_paused_to_normal() { + ExtBuilder {}.build().execute_with(|| { + Mode::::set(XcmMode::Paused); + let call: RuntimeCall = Call::paused_to_normal {}.into(); + assert_ok!(call.dispatch(RuntimeOrigin::root())); + let xcm_mode = EmergencyParaXcm::mode(); + assert!(xcm_mode == XcmMode::Normal); + }); +}