From 9f7daa1766e0286286ae9e48ddb5ea3acc5e4f9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89lo=C3=AFs?= Date: Fri, 30 Aug 2024 12:26:17 +0200 Subject: [PATCH] Fix auto-pause xcm: incoming XCMP messages where dropped when auto-pause (#2913) * chore: rename crate manual-xcm-rpc -> moonbeam-dev-rpc * add RPC method test_skipRelayBlocks * allow Root to resume XCM execution * update moonkit pin * add dev-test that trigger auto-pause of xcm * prettier * dev test auto-pause xcm: remove unused imports & improve test scenario * add pallet-emergency-para-xcm to moonriver * add pallet-emergency-para-xcm to moonbeam * apply review suggestions --- Cargo.toml | 4 +- client/rpc/{manual-xcm => dev}/Cargo.toml | 2 +- client/rpc/{manual-xcm => dev}/src/lib.rs | 22 ++- node/service/Cargo.toml | 2 +- node/service/src/lib.rs | 17 +- node/service/src/rpc.rs | 17 +- runtime/moonbase/src/lib.rs | 2 +- runtime/moonbase/src/xcm_config.rs | 16 +- runtime/moonbeam/Cargo.toml | 2 + runtime/moonbeam/src/lib.rs | 8 +- runtime/moonbeam/src/xcm_config.rs | 29 +++- runtime/moonriver/Cargo.toml | 2 + runtime/moonriver/src/lib.rs | 8 +- runtime/moonriver/src/xcm_config.rs | 29 +++- .../test-xcm-v4/test-auto-pause-xcm.ts | 146 ++++++++++++++++++ 15 files changed, 265 insertions(+), 41 deletions(-) rename client/rpc/{manual-xcm => dev}/Cargo.toml (96%) rename client/rpc/{manual-xcm => dev}/src/lib.rs (90%) create mode 100644 test/suites/dev/moonbase/test-xcm-v4/test-auto-pause-xcm.ts diff --git a/Cargo.toml b/Cargo.toml index cd829a77c3..2d723023ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,8 +2,8 @@ exclude = ["bin/utils/moonkey"] members = [ "bin/utils/moonkey", + "client/rpc/dev", "client/rpc/finality", - "client/rpc/manual-xcm", "client/vrf", "node", "node/cli", @@ -117,8 +117,8 @@ moonbeam-cli = { path = "node/cli", default-features = false } moonbeam-cli-opt = { path = "node/cli-opt", default-features = false } moonbeam-service = { path = "node/service", default-features = false } -manual-xcm-rpc = { path = "client/rpc/manual-xcm" } moonbeam-client-evm-tracing = { path = "client/evm-tracing" } +moonbeam-dev-rpc = { path = "client/rpc/dev" } moonbeam-finality-rpc = { path = "client/rpc/finality" } moonbeam-rpc-core-debug = { path = "client/rpc-core/debug" } moonbeam-rpc-core-trace = { path = "client/rpc-core/trace" } diff --git a/client/rpc/manual-xcm/Cargo.toml b/client/rpc/dev/Cargo.toml similarity index 96% rename from client/rpc/manual-xcm/Cargo.toml rename to client/rpc/dev/Cargo.toml index 010bdd1669..87558951a0 100644 --- a/client/rpc/manual-xcm/Cargo.toml +++ b/client/rpc/dev/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "manual-xcm-rpc" +name = "moonbeam-dev-rpc" authors = { workspace = true } edition = "2021" homepage = "https://moonbeam.network" diff --git a/client/rpc/manual-xcm/src/lib.rs b/client/rpc/dev/src/lib.rs similarity index 90% rename from client/rpc/manual-xcm/src/lib.rs rename to client/rpc/dev/src/lib.rs index bc65717144..6ac9725ecc 100644 --- a/client/rpc/manual-xcm/src/lib.rs +++ b/client/rpc/dev/src/lib.rs @@ -13,6 +13,7 @@ // You should have received a copy of the GNU General Public License // along with Moonbeam. If not, see . + use cumulus_primitives_core::ParaId; use cumulus_primitives_core::XcmpMessageFormat; use jsonrpsee::{ @@ -28,12 +29,10 @@ use xcm::opaque::lts::Weight; use xcm::v4::prelude::*; use xcm_primitives::DEFAULT_PROOF_SIZE; -/// This RPC interface is used to manually submit XCM messages that will be injected into a -/// parachain-enabled runtime. This allows testing XCM logic in a controlled way in integration -/// tests. +/// This RPC interface is used to provide methods in dev mode only #[rpc(server)] #[jsonrpsee::core::async_trait] -pub trait ManualXcmApi { +pub trait DevApi { /// Inject a downward xcm message - A message that comes from the relay chain. /// You may provide an arbitrary message, or if you provide an emtpy byte array, /// Then a default message (DOT transfer down to ALITH) will be injected @@ -53,15 +52,20 @@ pub trait ManualXcmApi { /// transfer of the sending paraId's native token will be injected. #[method(name = "xcm_injectHrmpMessage")] async fn inject_hrmp_message(&self, sender: ParaId, message: Vec) -> RpcResult<()>; + + /// Skip N relay blocks, for testing purposes + #[method(name = "test_skipRelayBlocks")] + async fn skip_relay_blocks(&self, n: u32) -> RpcResult<()>; } -pub struct ManualXcm { +pub struct DevRpc { pub downward_message_channel: flume::Sender>, pub hrmp_message_channel: flume::Sender<(ParaId, Vec)>, + pub additional_relay_offset: std::sync::Arc, } #[jsonrpsee::core::async_trait] -impl ManualXcmApiServer for ManualXcm { +impl DevApiServer for DevRpc { async fn inject_downward_message(&self, msg: Vec) -> RpcResult<()> { let downward_message_channel = self.downward_message_channel.clone(); // If no message is supplied, inject a default one. @@ -148,6 +152,12 @@ impl ManualXcmApiServer for ManualXcm { Ok(()) } + + async fn skip_relay_blocks(&self, n: u32) -> RpcResult<()> { + self.additional_relay_offset + .fetch_add(n, std::sync::atomic::Ordering::SeqCst); + Ok(()) + } } // This bit cribbed from frontier. diff --git a/node/service/Cargo.toml b/node/service/Cargo.toml index ca8daa318d..08d49836ec 100644 --- a/node/service/Cargo.toml +++ b/node/service/Cargo.toml @@ -28,7 +28,7 @@ tokio = { workspace = true, features = ["macros", "sync"] } trie-root = { workspace = true } # Moonbeam -manual-xcm-rpc = { workspace = true } +moonbeam-dev-rpc = { workspace = true } moonbeam-cli-opt = { workspace = true } moonbeam-core-primitives = { workspace = true } moonbeam-finality-rpc = { workspace = true } diff --git a/node/service/src/lib.rs b/node/service/src/lib.rs index e9ffe93663..5c15b886fc 100644 --- a/node/service/src/lib.rs +++ b/node/service/src/lib.rs @@ -774,7 +774,7 @@ where fee_history_cache: fee_history_cache.clone(), network: network.clone(), sync: sync.clone(), - xcm_senders: None, + dev_rpc_data: None, block_data_cache: block_data_cache.clone(), overrides: overrides.clone(), forced_parent_hashes, @@ -1204,7 +1204,7 @@ where let overrides = Arc::new(StorageOverrideHandler::new(client.clone())); let fee_history_limit = rpc_config.fee_history_limit; let mut command_sink = None; - let mut xcm_senders = None; + let mut dev_rpc_data = None; let collator = config.role.is_authority(); if collator { @@ -1269,7 +1269,12 @@ where // Create channels for mocked XCM messages. let (downward_xcm_sender, downward_xcm_receiver) = flume::bounded::>(100); let (hrmp_xcm_sender, hrmp_xcm_receiver) = flume::bounded::<(ParaId, Vec)>(100); - xcm_senders = Some((downward_xcm_sender, hrmp_xcm_sender)); + let additional_relay_offset = Arc::new(std::sync::atomic::AtomicU32::new(0)); + dev_rpc_data = Some(( + downward_xcm_sender, + hrmp_xcm_sender, + additional_relay_offset.clone(), + )); let client_clone = client.clone(); let keystore_clone = keystore_container.keystore().clone(); @@ -1304,6 +1309,7 @@ where let maybe_current_para_head = client_set_aside_for_cidp.expect_header(block); let downward_xcm_receiver = downward_xcm_receiver.clone(); let hrmp_xcm_receiver = hrmp_xcm_receiver.clone(); + let additional_relay_offset = additional_relay_offset.clone(); let client_for_xcm = client_set_aside_for_cidp.clone(); async move { @@ -1324,7 +1330,8 @@ where let mocked_parachain = MockValidationDataInherentDataProvider { current_para_block, current_para_block_head, - relay_offset: 1000, + relay_offset: 1000 + + additional_relay_offset.load(std::sync::atomic::Ordering::SeqCst), relay_blocks_per_para_block: 2, // TODO: Recheck para_blocks_per_relay_epoch: 10, @@ -1440,7 +1447,7 @@ where fee_history_cache: fee_history_cache.clone(), network: network.clone(), sync: sync.clone(), - xcm_senders: xcm_senders.clone(), + dev_rpc_data: dev_rpc_data.clone(), overrides: overrides.clone(), block_data_cache: block_data_cache.clone(), forced_parent_hashes: None, diff --git a/node/service/src/rpc.rs b/node/service/src/rpc.rs index 9ab4095fc0..92043cd9cf 100644 --- a/node/service/src/rpc.rs +++ b/node/service/src/rpc.rs @@ -130,7 +130,11 @@ pub struct FullDeps { /// Fee history cache. pub fee_history_cache: FeeHistoryCache, /// Channels for manual xcm messages (downward, hrmp) - pub xcm_senders: Option<(flume::Sender>, flume::Sender<(ParaId, Vec)>)>, + pub dev_rpc_data: Option<( + flume::Sender>, + flume::Sender<(ParaId, Vec)>, + Arc, + )>, /// Ethereum data access overrides. pub overrides: Arc>, /// Cache for Ethereum block data. @@ -173,7 +177,7 @@ where Eth, EthApiServer, EthFilter, EthFilterApiServer, EthPubSub, EthPubSubApiServer, Net, NetApiServer, Web3, Web3ApiServer, }; - use manual_xcm_rpc::{ManualXcm, ManualXcmApiServer}; + use moonbeam_dev_rpc::{DevApiServer, DevRpc}; use moonbeam_finality_rpc::{MoonbeamFinality, MoonbeamFinalityApiServer}; use moonbeam_rpc_debug::{Debug, DebugServer}; use moonbeam_rpc_trace::{Trace, TraceServer}; @@ -198,7 +202,7 @@ where max_past_logs, fee_history_limit, fee_history_cache, - xcm_senders, + dev_rpc_data, overrides, block_data_cache, forced_parent_hashes, @@ -318,11 +322,14 @@ where )?; }; - if let Some((downward_message_channel, hrmp_message_channel)) = xcm_senders { + if let Some((downward_message_channel, hrmp_message_channel, additional_relay_offset)) = + dev_rpc_data + { io.merge( - ManualXcm { + DevRpc { downward_message_channel, hrmp_message_channel, + additional_relay_offset, } .into_rpc(), )?; diff --git a/runtime/moonbase/src/lib.rs b/runtime/moonbase/src/lib.rs index 0255c36599..8d7b9c953a 100644 --- a/runtime/moonbase/src/lib.rs +++ b/runtime/moonbase/src/lib.rs @@ -754,7 +754,7 @@ impl cumulus_pallet_parachain_system::Config for Runtime { type SelfParaId = ParachainInfo; type ReservedDmpWeight = ReservedDmpWeight; type OutboundXcmpMessageSource = XcmpQueue; - type XcmpMessageHandler = EmergencyParaXcm; + type XcmpMessageHandler = XcmpQueue; type ReservedXcmpWeight = ReservedXcmpWeight; type CheckAssociatedRelayNumber = EmergencyParaXcm; type ConsensusHook = ConsensusHookWrapperForRelayTimestamp; diff --git a/runtime/moonbase/src/xcm_config.rs b/runtime/moonbase/src/xcm_config.rs index 2c38ee6109..b1d3b87982 100644 --- a/runtime/moonbase/src/xcm_config.rs +++ b/runtime/moonbase/src/xcm_config.rs @@ -464,16 +464,24 @@ impl pallet_message_queue::Config for Runtime { type IdleMaxServiceWeight = MessageQueueServiceWeight; } +pub type FastAuthorizeUpgradeOrigin = EitherOfDiverse< + EnsureRoot, + pallet_collective::EnsureProportionAtLeast, +>; + +pub type ResumeXcmOrigin = EitherOfDiverse< + EnsureRoot, + pallet_collective::EnsureProportionAtLeast, +>; + impl pallet_emergency_para_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; type CheckAssociatedRelayNumber = cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; type QueuePausedQuery = (MaintenanceMode, NarrowOriginToSibling); type PausedThreshold = ConstU32<300>; - type FastAuthorizeUpgradeOrigin = - pallet_collective::EnsureProportionAtLeast; - type PausedToNormalOrigin = - pallet_collective::EnsureProportionAtLeast; + type FastAuthorizeUpgradeOrigin = FastAuthorizeUpgradeOrigin; + type PausedToNormalOrigin = ResumeXcmOrigin; } // Our AssetType. For now we only handle Xcm Assets diff --git a/runtime/moonbeam/Cargo.toml b/runtime/moonbeam/Cargo.toml index 0da4254009..40b0b1bb03 100644 --- a/runtime/moonbeam/Cargo.toml +++ b/runtime/moonbeam/Cargo.toml @@ -32,6 +32,7 @@ moonbeam-xcm-benchmarks = { workspace = true } pallet-asset-manager = { workspace = true } pallet-author-mapping = { workspace = true } pallet-crowdloan-rewards = { workspace = true } +pallet-emergency-para-xcm = { workspace = true } pallet-erc20-xcm-bridge = { workspace = true } pallet-ethereum-xcm = { workspace = true } pallet-evm-chain-id = { workspace = true } @@ -233,6 +234,7 @@ std = [ "pallet-collective/std", "pallet-conviction-voting/std", "pallet-crowdloan-rewards/std", + "pallet-emergency-para-xcm/std", "pallet-erc20-xcm-bridge/std", "pallet-evm-chain-id/std", "pallet-ethereum-xcm/std", diff --git a/runtime/moonbeam/src/lib.rs b/runtime/moonbeam/src/lib.rs index f2bed55caa..0818750e2d 100644 --- a/runtime/moonbeam/src/lib.rs +++ b/runtime/moonbeam/src/lib.rs @@ -21,8 +21,8 @@ //! * Moonbeam tokenomics #![cfg_attr(not(feature = "std"), no_std)] -// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. -#![recursion_limit = "256"] +// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 512. +#![recursion_limit = "512"] // Make the WASM binary available. #[cfg(feature = "std")] @@ -704,8 +704,7 @@ impl cumulus_pallet_parachain_system::Config for Runtime { type OutboundXcmpMessageSource = XcmpQueue; type XcmpMessageHandler = XcmpQueue; type ReservedXcmpWeight = ReservedXcmpWeight; - type CheckAssociatedRelayNumber = - cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; + type CheckAssociatedRelayNumber = EmergencyParaXcm; type ConsensusHook = ConsensusHookWrapperForRelayTimestamp; type DmpQueue = frame_support::traits::EnqueueWithOrigin; type WeightInfo = moonbeam_weights::cumulus_pallet_parachain_system::WeightInfo; @@ -1448,6 +1447,7 @@ construct_runtime! { Erc20XcmBridge: pallet_erc20_xcm_bridge::{Pallet} = 110, MessageQueue: pallet_message_queue::{Pallet, Call, Storage, Event} = 111, EvmForeignAssets: pallet_moonbeam_foreign_assets::{Pallet, Call, Storage, Event} = 114, + EmergencyParaXcm: pallet_emergency_para_xcm::{Pallet, Call, Storage, Event} = 116, // Utils RelayStorageRoots: pallet_relay_storage_roots::{Pallet, Storage} = 112, diff --git a/runtime/moonbeam/src/xcm_config.rs b/runtime/moonbeam/src/xcm_config.rs index c7b997fda9..c189d3683f 100644 --- a/runtime/moonbeam/src/xcm_config.rs +++ b/runtime/moonbeam/src/xcm_config.rs @@ -18,9 +18,10 @@ //! use super::{ - governance, AccountId, AssetId, AssetManager, Balance, Balances, DealWithFees, Erc20XcmBridge, - MaintenanceMode, MessageQueue, ParachainInfo, ParachainSystem, Perbill, PolkadotXcm, Runtime, - RuntimeBlockWeights, RuntimeCall, RuntimeEvent, RuntimeOrigin, Treasury, XcmpQueue, + governance, AccountId, AssetId, AssetManager, Balance, Balances, DealWithFees, + EmergencyParaXcm, Erc20XcmBridge, MaintenanceMode, MessageQueue, OpenTechCommitteeInstance, + ParachainInfo, ParachainSystem, Perbill, PolkadotXcm, Runtime, RuntimeBlockWeights, + RuntimeCall, RuntimeEvent, RuntimeOrigin, Treasury, XcmpQueue, }; use frame_support::{ @@ -447,11 +448,31 @@ impl pallet_message_queue::Config for Runtime { // The XCMP queue pallet is only ever able to handle the `Sibling(ParaId)` origin: type QueueChangeHandler = NarrowOriginToSibling; // NarrowOriginToSibling calls XcmpQueue's is_paused if Origin is sibling. Allows all other origins - type QueuePausedQuery = (MaintenanceMode, NarrowOriginToSibling); + type QueuePausedQuery = EmergencyParaXcm; type WeightInfo = moonbeam_weights::pallet_message_queue::WeightInfo; type IdleMaxServiceWeight = MessageQueueServiceWeight; } +pub type FastAuthorizeUpgradeOrigin = EitherOfDiverse< + EnsureRoot, + pallet_collective::EnsureProportionAtLeast, +>; + +pub type ResumeXcmOrigin = EitherOfDiverse< + EnsureRoot, + pallet_collective::EnsureProportionAtLeast, +>; + +impl pallet_emergency_para_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type CheckAssociatedRelayNumber = + cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; + type QueuePausedQuery = (MaintenanceMode, NarrowOriginToSibling); + type PausedThreshold = ConstU32<300>; + type FastAuthorizeUpgradeOrigin = FastAuthorizeUpgradeOrigin; + type PausedToNormalOrigin = ResumeXcmOrigin; +} + // Our AssetType. For now we only handle Xcm Assets #[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] pub enum AssetType { diff --git a/runtime/moonriver/Cargo.toml b/runtime/moonriver/Cargo.toml index 60aa5a3af4..499608f449 100644 --- a/runtime/moonriver/Cargo.toml +++ b/runtime/moonriver/Cargo.toml @@ -32,6 +32,7 @@ moonbeam-xcm-benchmarks = { workspace = true } pallet-asset-manager = { workspace = true } pallet-author-mapping = { workspace = true } pallet-crowdloan-rewards = { workspace = true } +pallet-emergency-para-xcm = { workspace = true } pallet-erc20-xcm-bridge = { workspace = true } pallet-ethereum-xcm = { workspace = true } pallet-evm-chain-id = { workspace = true } @@ -234,6 +235,7 @@ std = [ "pallet-collective/std", "pallet-conviction-voting/std", "pallet-crowdloan-rewards/std", + "pallet-emergency-para-xcm/std", "pallet-erc20-xcm-bridge/std", "pallet-evm-chain-id/std", "pallet-ethereum-xcm/std", diff --git a/runtime/moonriver/src/lib.rs b/runtime/moonriver/src/lib.rs index b63b1237f3..9055233b05 100644 --- a/runtime/moonriver/src/lib.rs +++ b/runtime/moonriver/src/lib.rs @@ -21,8 +21,8 @@ //! * Moonriver tokenomics #![cfg_attr(not(feature = "std"), no_std)] -// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. -#![recursion_limit = "256"] +// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 512. +#![recursion_limit = "512"] // Make the WASM binary available. #[cfg(feature = "std")] @@ -740,8 +740,7 @@ impl cumulus_pallet_parachain_system::Config for Runtime { type OutboundXcmpMessageSource = XcmpQueue; type XcmpMessageHandler = XcmpQueue; type ReservedXcmpWeight = ReservedXcmpWeight; - type CheckAssociatedRelayNumber = - cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; + type CheckAssociatedRelayNumber = EmergencyParaXcm; type ConsensusHook = ConsensusHookWrapperForRelayTimestamp; type DmpQueue = frame_support::traits::EnqueueWithOrigin; type WeightInfo = moonriver_weights::cumulus_pallet_parachain_system::WeightInfo; @@ -1451,6 +1450,7 @@ construct_runtime! { Erc20XcmBridge: pallet_erc20_xcm_bridge::{Pallet} = 110, MessageQueue: pallet_message_queue::{Pallet, Call, Storage, Event} = 111, EvmForeignAssets: pallet_moonbeam_foreign_assets::{Pallet, Call, Storage, Event} = 114, + EmergencyParaXcm: pallet_emergency_para_xcm::{Pallet, Call, Storage, Event} = 116, // Utils RelayStorageRoots: pallet_relay_storage_roots::{Pallet, Storage} = 112, diff --git a/runtime/moonriver/src/xcm_config.rs b/runtime/moonriver/src/xcm_config.rs index a4febc68f8..54150cbe60 100644 --- a/runtime/moonriver/src/xcm_config.rs +++ b/runtime/moonriver/src/xcm_config.rs @@ -18,9 +18,10 @@ //! use super::{ - governance, AccountId, AssetId, AssetManager, Balance, Balances, DealWithFees, Erc20XcmBridge, - MaintenanceMode, MessageQueue, ParachainInfo, ParachainSystem, Perbill, PolkadotXcm, Runtime, - RuntimeBlockWeights, RuntimeCall, RuntimeEvent, RuntimeOrigin, Treasury, XcmpQueue, + governance, AccountId, AssetId, AssetManager, Balance, Balances, DealWithFees, + EmergencyParaXcm, Erc20XcmBridge, MaintenanceMode, MessageQueue, OpenTechCommitteeInstance, + ParachainInfo, ParachainSystem, Perbill, PolkadotXcm, Runtime, RuntimeBlockWeights, + RuntimeCall, RuntimeEvent, RuntimeOrigin, Treasury, XcmpQueue, }; use frame_support::{ @@ -455,11 +456,31 @@ impl pallet_message_queue::Config for Runtime { // The XCMP queue pallet is only ever able to handle the `Sibling(ParaId)` origin: type QueueChangeHandler = NarrowOriginToSibling; // NarrowOriginToSibling calls XcmpQueue's is_paused if Origin is sibling. Allows all other origins - type QueuePausedQuery = (MaintenanceMode, NarrowOriginToSibling); + type QueuePausedQuery = EmergencyParaXcm; type WeightInfo = moonriver_weights::pallet_message_queue::WeightInfo; type IdleMaxServiceWeight = MessageQueueServiceWeight; } +pub type FastAuthorizeUpgradeOrigin = EitherOfDiverse< + EnsureRoot, + pallet_collective::EnsureProportionAtLeast, +>; + +pub type ResumeXcmOrigin = EitherOfDiverse< + EnsureRoot, + pallet_collective::EnsureProportionAtLeast, +>; + +impl pallet_emergency_para_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type CheckAssociatedRelayNumber = + cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; + type QueuePausedQuery = (MaintenanceMode, NarrowOriginToSibling); + type PausedThreshold = ConstU32<300>; + type FastAuthorizeUpgradeOrigin = FastAuthorizeUpgradeOrigin; + type PausedToNormalOrigin = ResumeXcmOrigin; +} + // Our AssetType. For now we only handle Xcm Assets #[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] pub enum AssetType { diff --git a/test/suites/dev/moonbase/test-xcm-v4/test-auto-pause-xcm.ts b/test/suites/dev/moonbase/test-xcm-v4/test-auto-pause-xcm.ts new file mode 100644 index 0000000000..29c91c0b37 --- /dev/null +++ b/test/suites/dev/moonbase/test-xcm-v4/test-auto-pause-xcm.ts @@ -0,0 +1,146 @@ +import "@moonbeam-network/api-augment"; +import { beforeAll, customDevRpcRequest, describeSuite, expect } from "@moonwall/cli"; + +import { KeyringPair } from "@polkadot/keyring/types"; +import { generateKeyringPair } from "@moonwall/util"; +import { + XcmFragment, + RawXcmMessage, + sovereignAccountOfSibling, + injectHrmpMessage, +} from "../../../../helpers/xcm.js"; + +const foreign_para_id = 2000; + +describeSuite({ + id: "D014134", + title: "Auto-pause XCM", + foundationMethods: "dev", + testCases: ({ context, it, log }) => { + let transferredBalance: bigint; + let sovereignAddress: string; + let random: KeyringPair; + let balancesPalletIndex: number; + + beforeAll(async () => { + random = generateKeyringPair(); + sovereignAddress = sovereignAccountOfSibling(context, 2000); + transferredBalance = 1_000_000_000_000_000n; + + await context.createBlock( + context.polkadotJs().tx.balances.transferAllowDeath(sovereignAddress, transferredBalance), + { allowFailures: false } + ); + + const balance = ( + await context.polkadotJs().query.system.account(sovereignAddress) + ).data.free.toBigInt(); + expect(balance).to.eq(transferredBalance); + + const metadata = await context.polkadotJs().rpc.state.getMetadata(); + balancesPalletIndex = metadata.asLatest.pallets + .find(({ name }) => name.toString() == "Balances")! + .index.toNumber(); + }); + + it({ + id: "T01", + title: "Should automatically pause xcm when block production is stuck", + test: async function () { + await context.createBlock(); + + // XCM Mode should be equal to Normal + expect((await context.polkadotJs().query.emergencyParaXcm.mode()).isNormal).to.be.true; + + // Create a dummy xcm message to test auto-pause + const xcmMessage = new XcmFragment({ + assets: [ + { + multilocation: { + parents: 0, + interior: { + X1: { PalletInstance: balancesPalletIndex }, + }, + }, + fungible: transferredBalance, + }, + ], + beneficiary: random.address, + }) + .withdraw_asset() + .clear_origin() + .buy_execution() + .deposit_asset_v3() + .as_v4(); + + // Inject an XCM message that should be included in the next block but not executed + await injectHrmpMessage(context, foreign_para_id, { + type: "XcmVersionedXcm", + payload: xcmMessage, + } as RawXcmMessage); + + // Simulate block production stall (skip more than PausedThreshold relay blocks) + await customDevRpcRequest("test_skipRelayBlocks", [301]); + + // Create a new block, this block should pause XCM incoming execution + await context.createBlock([], { + expectEvents: [context.polkadotJs().events.emergencyParaXcm.EnteredPausedXcmMode], + allowFailures: false, + }); + + // XCM Mode should be equal to Paused + expect((await context.polkadotJs().query.emergencyParaXcm.mode()).isPaused).to.be.true; + + // Produce some blocks when XCm is Paused + await context.createBlock(); + await context.createBlock(); + + // The sovereign account of foreign parachain sould still have funds + const balance = ( + await context.polkadotJs().query.system.account(sovereignAddress) + ).data.free.toBigInt(); + expect(balance, "Sovereign account balance has changed").to.eq(transferredBalance); + + // The beneficiary of the XCM message should not have funds + const randomBalance = ( + await context.polkadotJs().query.system.account(random.address) + ).data.free.toBigInt(); + expect(randomBalance, "beneficiary of the XCM message receive funds").to.eq(0n); + + // Sudo should be able to resume XCM execution + await context.createBlock( + context + .polkadotJs() + .tx.sudo.sudo(context.polkadotJs().tx.emergencyParaXcm.pausedToNormal()), + { + expectEvents: [context.polkadotJs().events.emergencyParaXcm.NormalXcmOperationResumed], + allowFailures: false, + } + ); + + // XCM Mode should be equal to Normal + expect((await context.polkadotJs().query.emergencyParaXcm.mode()).isNormal).to.be.true; + + // The next block should execute previous XCM message + await context.createBlock([], { + expectEvents: [], + allowFailures: false, + }); + + // The sovereign account of foreign parachain should now be empty + const balance2 = ( + await context.polkadotJs().query.system.account(sovereignAddress) + ).data.free.toBigInt(); + expect(balance2, "Sovereign account not empty, transfer has failed").to.eq(0n); + + // The beneficiary of the XCM message should now have funds + const randomBalance2 = ( + await context.polkadotJs().query.system.account(random.address) + ).data.free.toBigInt(); + expect(randomBalance2, "beneficiary balance not increased, transfer has failed").to.not.eq( + 0n + ); + }, + }); + }, +});