diff --git a/runtime/kusama/src/weights/runtime_parachains_hrmp.rs b/runtime/kusama/src/weights/runtime_parachains_hrmp.rs index c7879ac8f17c..ab7a5b87291e 100644 --- a/runtime/kusama/src/weights/runtime_parachains_hrmp.rs +++ b/runtime/kusama/src/weights/runtime_parachains_hrmp.rs @@ -156,4 +156,19 @@ impl runtime_parachains::hrmp::WeightInfo for WeightInf .saturating_add(T::DbWeight::get().writes(1 as u64)) .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(c as u64))) } + // Storage: Paras ParaLifecycles (r:2 w:0) + // Storage: Hrmp HrmpOpenChannelRequests (r:1 w:1) + // Storage: Hrmp HrmpChannels (r:1 w:0) + // Storage: Hrmp HrmpEgressChannelsIndex (r:1 w:0) + // Storage: Hrmp HrmpOpenChannelRequestCount (r:1 w:1) + // Storage: Hrmp HrmpOpenChannelRequestsList (r:1 w:1) + // Storage: Dmp DownwardMessageQueueHeads (r:2 w:2) + // Storage: Dmp DownwardMessageQueues (r:2 w:2) + // Storage: Hrmp HrmpIngressChannelsIndex (r:1 w:0) + // Storage: Hrmp HrmpAcceptedChannelRequestCount (r:1 w:1) + fn force_open_hrmp_channel() -> Weight { + Weight::from_ref_time(104_771_000 as u64) + .saturating_add(T::DbWeight::get().reads(13 as u64)) + .saturating_add(T::DbWeight::get().writes(8 as u64)) + } } diff --git a/runtime/parachains/src/hrmp.rs b/runtime/parachains/src/hrmp.rs index af0cccb22512..53ad6781048f 100644 --- a/runtime/parachains/src/hrmp.rs +++ b/runtime/parachains/src/hrmp.rs @@ -58,6 +58,7 @@ pub trait WeightInfo { fn force_process_hrmp_close(c: u32) -> Weight; fn hrmp_cancel_open_request(c: u32) -> Weight; fn clean_open_channel_requests(c: u32) -> Weight; + fn force_open_hrmp_channel() -> Weight; } /// A weight info that is only suitable for testing. @@ -88,6 +89,9 @@ impl WeightInfo for TestWeightInfo { fn clean_open_channel_requests(_: u32) -> Weight { Weight::MAX } + fn force_open_hrmp_channel() -> Weight { + Weight::MAX + } } /// A description of a request to open an HRMP channel. @@ -269,6 +273,9 @@ pub mod pallet { OpenChannelAccepted(ParaId, ParaId), /// HRMP channel closed. `[by_parachain, channel_id]` ChannelClosed(ParaId, HrmpChannelId), + /// An HRMP channel was opened via Root origin. + /// `[sender, recipient, proposed_max_capacity, proposed_max_message_size]` + HrmpChannelForceOpened(ParaId, ParaId, u32, u32), } #[pallet::error] @@ -577,6 +584,32 @@ pub mod pallet { Self::deposit_event(Event::OpenChannelCanceled(origin, channel_id)); Ok(()) } + + /// Open a channel from a `sender` to a `recipient` `ParaId` using the Root origin. Although + /// opened by Root, the `max_capacity` and `max_message_size` are still subject to the Relay + /// Chain's configured limits. + /// + /// Expected use is when one of the `ParaId`s involved in the channel is governed by the + /// Relay Chain, e.g. a common good parachain. + #[pallet::weight(::WeightInfo::force_open_hrmp_channel())] + pub fn force_open_hrmp_channel( + origin: OriginFor, + sender: ParaId, + recipient: ParaId, + max_capacity: u32, + max_message_size: u32, + ) -> DispatchResult { + ensure_root(origin)?; + Self::init_open_channel(sender, recipient, max_capacity, max_message_size)?; + Self::accept_open_channel(recipient, sender)?; + Self::deposit_event(Event::HrmpChannelForceOpened( + sender, + recipient, + max_capacity, + max_message_size, + )); + Ok(()) + } } } diff --git a/runtime/parachains/src/hrmp/benchmarking.rs b/runtime/parachains/src/hrmp/benchmarking.rs index 71ea684be179..7ea14b1dc922 100644 --- a/runtime/parachains/src/hrmp/benchmarking.rs +++ b/runtime/parachains/src/hrmp/benchmarking.rs @@ -296,6 +296,32 @@ frame_benchmarking::benchmarks! { } verify { assert_eq!(HrmpOpenChannelRequestsList::::decode_len().unwrap_or_default() as u32, 0); } + + force_open_hrmp_channel { + let sender_id: ParaId = 1u32.into(); + let recipient_id: ParaId = 2u32.into(); + + // make sure para is registered, and has enough balance. + let sender_deposit: BalanceOf = + Configuration::::config().hrmp_sender_deposit.unique_saturated_into(); + let recipient_deposit: BalanceOf = + Configuration::::config().hrmp_recipient_deposit.unique_saturated_into(); + register_parachain_with_balance::(sender_id, sender_deposit); + register_parachain_with_balance::(recipient_id, recipient_deposit); + + let capacity = Configuration::::config().hrmp_channel_max_capacity; + let message_size = Configuration::::config().hrmp_channel_max_message_size; + + // make sure this channel doesn't exist + let channel_id = HrmpChannelId { sender: sender_id, recipient: recipient_id }; + assert!(HrmpOpenChannelRequests::::get(&channel_id).is_none()); + assert!(HrmpChannels::::get(&channel_id).is_none()); + }: _(frame_system::Origin::::Root, sender_id, recipient_id, capacity, message_size) + verify { + assert_last_event::( + Event::::HrmpChannelForceOpened(sender_id, recipient_id, capacity, message_size).into() + ); + } } frame_benchmarking::impl_benchmark_test_suite!( diff --git a/runtime/parachains/src/hrmp/tests.rs b/runtime/parachains/src/hrmp/tests.rs index 22824601b521..9e0d0646d057 100644 --- a/runtime/parachains/src/hrmp/tests.rs +++ b/runtime/parachains/src/hrmp/tests.rs @@ -17,7 +17,7 @@ use super::*; use crate::mock::{ new_test_ext, Configuration, Hrmp, MockGenesisConfig, Paras, ParasShared, - RuntimeEvent as MockEvent, System, Test, + RuntimeEvent as MockEvent, RuntimeOrigin, System, Test, }; use frame_support::{assert_noop, assert_ok, traits::Currency as _}; use primitives::v2::BlockNumber; @@ -194,6 +194,34 @@ fn open_channel_works() { }); } +#[test] +fn force_open_channel_works() { + let para_a = 1.into(); + let para_b = 3.into(); + + new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| { + // We need both A & B to be registered and live parachains. + register_parachain(para_a); + register_parachain(para_b); + + run_to_block(5, Some(vec![4, 5])); + Hrmp::force_open_hrmp_channel(RuntimeOrigin::root(), para_a, para_b, 2, 8).unwrap(); + Hrmp::assert_storage_consistency_exhaustive(); + assert!(System::events().iter().any(|record| record.event == + MockEvent::Hrmp(Event::HrmpChannelForceOpened(para_a, para_b, 2, 8)))); + + // Advance to a block 6, but without session change. That means that the channel has + // not been created yet. + run_to_block(6, None); + assert!(!channel_exists(para_a, para_b)); + Hrmp::assert_storage_consistency_exhaustive(); + + // Now let the session change happen and thus open the channel. + run_to_block(8, Some(vec![8])); + assert!(channel_exists(para_a, para_b)); + }); +} + #[test] fn close_channel_works() { let para_a = 5.into(); diff --git a/runtime/polkadot/src/lib.rs b/runtime/polkadot/src/lib.rs index 47d6325a49b2..fad136bc9da9 100644 --- a/runtime/polkadot/src/lib.rs +++ b/runtime/polkadot/src/lib.rs @@ -1594,6 +1594,7 @@ mod benches { [runtime_common::paras_registrar, Registrar] [runtime_parachains::configuration, Configuration] [runtime_parachains::disputes, ParasDisputes] + [runtime_parachains::hrmp, Hrmp] [runtime_parachains::initializer, Initializer] [runtime_parachains::paras, Paras] [runtime_parachains::paras_inherent, ParaInherent] diff --git a/runtime/polkadot/src/weights/runtime_parachains_hrmp.rs b/runtime/polkadot/src/weights/runtime_parachains_hrmp.rs index 17d03bb637af..576b25ddd718 100644 --- a/runtime/polkadot/src/weights/runtime_parachains_hrmp.rs +++ b/runtime/polkadot/src/weights/runtime_parachains_hrmp.rs @@ -154,4 +154,19 @@ impl runtime_parachains::hrmp::WeightInfo for WeightInf .saturating_add(T::DbWeight::get().writes(1 as u64)) .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(c as u64))) } + // Storage: Paras ParaLifecycles (r:2 w:0) + // Storage: Hrmp HrmpOpenChannelRequests (r:1 w:1) + // Storage: Hrmp HrmpChannels (r:1 w:0) + // Storage: Hrmp HrmpEgressChannelsIndex (r:1 w:0) + // Storage: Hrmp HrmpOpenChannelRequestCount (r:1 w:1) + // Storage: Hrmp HrmpOpenChannelRequestsList (r:1 w:1) + // Storage: Dmp DownwardMessageQueueHeads (r:2 w:2) + // Storage: Dmp DownwardMessageQueues (r:2 w:2) + // Storage: Hrmp HrmpIngressChannelsIndex (r:1 w:0) + // Storage: Hrmp HrmpAcceptedChannelRequestCount (r:1 w:1) + fn force_open_hrmp_channel() -> Weight { + Weight::from_ref_time(104_771_000 as u64) + .saturating_add(T::DbWeight::get().reads(13 as u64)) + .saturating_add(T::DbWeight::get().writes(8 as u64)) + } } diff --git a/runtime/rococo/src/weights/runtime_parachains_hrmp.rs b/runtime/rococo/src/weights/runtime_parachains_hrmp.rs index 41e000095add..04db94e8f76a 100644 --- a/runtime/rococo/src/weights/runtime_parachains_hrmp.rs +++ b/runtime/rococo/src/weights/runtime_parachains_hrmp.rs @@ -160,4 +160,19 @@ impl runtime_parachains::hrmp::WeightInfo for WeightInf .saturating_add(T::DbWeight::get().writes(1 as u64)) .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(c as u64))) } + // Storage: Paras ParaLifecycles (r:2 w:0) + // Storage: Hrmp HrmpOpenChannelRequests (r:1 w:1) + // Storage: Hrmp HrmpChannels (r:1 w:0) + // Storage: Hrmp HrmpEgressChannelsIndex (r:1 w:0) + // Storage: Hrmp HrmpOpenChannelRequestCount (r:1 w:1) + // Storage: Hrmp HrmpOpenChannelRequestsList (r:1 w:1) + // Storage: Dmp DownwardMessageQueueHeads (r:2 w:2) + // Storage: Dmp DownwardMessageQueues (r:2 w:2) + // Storage: Hrmp HrmpIngressChannelsIndex (r:1 w:0) + // Storage: Hrmp HrmpAcceptedChannelRequestCount (r:1 w:1) + fn force_open_hrmp_channel() -> Weight { + Weight::from_ref_time(104_771_000 as u64) + .saturating_add(T::DbWeight::get().reads(13 as u64)) + .saturating_add(T::DbWeight::get().writes(8 as u64)) + } } diff --git a/runtime/westend/src/weights/runtime_parachains_hrmp.rs b/runtime/westend/src/weights/runtime_parachains_hrmp.rs index 67713cdf2f2d..5cb4754daa02 100644 --- a/runtime/westend/src/weights/runtime_parachains_hrmp.rs +++ b/runtime/westend/src/weights/runtime_parachains_hrmp.rs @@ -156,4 +156,19 @@ impl runtime_parachains::hrmp::WeightInfo for WeightInf .saturating_add(T::DbWeight::get().writes(1 as u64)) .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(c as u64))) } + // Storage: Paras ParaLifecycles (r:2 w:0) + // Storage: Hrmp HrmpOpenChannelRequests (r:1 w:1) + // Storage: Hrmp HrmpChannels (r:1 w:0) + // Storage: Hrmp HrmpEgressChannelsIndex (r:1 w:0) + // Storage: Hrmp HrmpOpenChannelRequestCount (r:1 w:1) + // Storage: Hrmp HrmpOpenChannelRequestsList (r:1 w:1) + // Storage: Dmp DownwardMessageQueueHeads (r:2 w:2) + // Storage: Dmp DownwardMessageQueues (r:2 w:2) + // Storage: Hrmp HrmpIngressChannelsIndex (r:1 w:0) + // Storage: Hrmp HrmpAcceptedChannelRequestCount (r:1 w:1) + fn force_open_hrmp_channel() -> Weight { + Weight::from_ref_time(104_771_000 as u64) + .saturating_add(T::DbWeight::get().reads(13 as u64)) + .saturating_add(T::DbWeight::get().writes(8 as u64)) + } }