Add Method to Establish HRMP Channels Among System Parachains (#1473)

Solution to establish HRMP channels between system parachains.

---------

Co-authored-by: Muharem Ismailov <ismailov.m.h@gmail.com>
This commit is contained in:
joe petrowski
2023-09-19 13:46:28 +02:00
committed by GitHub
parent f3061c14bf
commit 2d96c8d27c
9 changed files with 592 additions and 83 deletions
@@ -159,12 +159,6 @@ fn force_open_hrmp_channel_for_system_para_works() {
// Parachain A init values
let para_a_id = PenpalKusamaA::para_id();
let fund_amount = KUSAMA_ED * 1000_000_000;
// Fund Parachain's Sovereign accounts to be able to reserve the deposit
let para_a_sovereign_account = Kusama::fund_para_sovereign(fund_amount, para_a_id);
let system_para_sovereign_account = Kusama::fund_para_sovereign(fund_amount, system_para_id);
Kusama::execute_with(|| {
assert_ok!(<Kusama as KusamaPallet>::Hrmp::force_open_hrmp_channel(
relay_root_origin,
@@ -179,14 +173,6 @@ fn force_open_hrmp_channel_for_system_para_works() {
assert_expected_events!(
Kusama,
vec![
// Sender deposit is reserved for System Parachain's Sovereign account
RuntimeEvent::Balances(pallet_balances::Event::Reserved { who, .. }) =>{
who: *who == system_para_sovereign_account,
},
// Recipient deposit is reserved for Parachain's Sovereign account
RuntimeEvent::Balances(pallet_balances::Event::Reserved { who, .. }) =>{
who: *who == para_a_sovereign_account,
},
// HRMP channel forced opened
RuntimeEvent::Hrmp(
polkadot_runtime_parachains::hrmp::Event::HrmpChannelForceOpened(
@@ -159,12 +159,6 @@ fn force_open_hrmp_channel_for_system_para_works() {
// Parachain A init values
let para_a_id = PenpalPolkadotA::para_id();
let fund_amount = POLKADOT_ED * 1000_000_000;
// Fund Parachain's Sovereign accounts to be able to reserve the deposit
let system_para_sovereign_account = Polkadot::fund_para_sovereign(fund_amount, system_para_id);
let para_a_sovereign_account = Polkadot::fund_para_sovereign(fund_amount, para_a_id);
Polkadot::execute_with(|| {
assert_ok!(<Polkadot as PolkadotPallet>::Hrmp::force_open_hrmp_channel(
relay_root_origin,
@@ -179,14 +173,6 @@ fn force_open_hrmp_channel_for_system_para_works() {
assert_expected_events!(
Polkadot,
vec![
// Sender deposit is reserved for System Parachain's Sovereign account
RuntimeEvent::Balances(pallet_balances::Event::Reserved { who, .. }) =>{
who: *who == system_para_sovereign_account,
},
// Recipient deposit is reserved for Parachain's Sovereign account
RuntimeEvent::Balances(pallet_balances::Event::Reserved { who, .. }) =>{
who: *who == para_a_sovereign_account,
},
// HRMP channel forced opened
RuntimeEvent::Hrmp(
polkadot_runtime_parachains::hrmp::Event::HrmpChannelForceOpened(
@@ -282,4 +282,46 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
.saturating_add(T::DbWeight::get().reads(13))
.saturating_add(T::DbWeight::get().writes(8))
}
/// Storage: `Paras::ParaLifecycles` (r:1 w:0)
/// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Hrmp::HrmpOpenChannelRequests` (r:1 w:1)
/// Proof: `Hrmp::HrmpOpenChannelRequests` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Hrmp::HrmpChannels` (r:1 w:0)
/// Proof: `Hrmp::HrmpChannels` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Hrmp::HrmpEgressChannelsIndex` (r:1 w:0)
/// Proof: `Hrmp::HrmpEgressChannelsIndex` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Hrmp::HrmpOpenChannelRequestCount` (r:1 w:1)
/// Proof: `Hrmp::HrmpOpenChannelRequestCount` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Hrmp::HrmpOpenChannelRequestsList` (r:1 w:1)
/// Proof: `Hrmp::HrmpOpenChannelRequestsList` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
/// Storage: `Dmp::DownwardMessageQueues` (r:2 w:2)
/// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Dmp::DownwardMessageQueueHeads` (r:2 w:2)
/// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Hrmp::HrmpIngressChannelsIndex` (r:1 w:0)
/// Proof: `Hrmp::HrmpIngressChannelsIndex` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Hrmp::HrmpAcceptedChannelRequestCount` (r:1 w:1)
/// Proof: `Hrmp::HrmpAcceptedChannelRequestCount` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn establish_system_channel() -> Weight {
// Proof Size summary in bytes:
// Measured: `417`
// Estimated: `6357`
// Minimum execution time: 629_674_000 picoseconds.
Weight::from_parts(640_174_000, 0)
.saturating_add(Weight::from_parts(0, 6357))
.saturating_add(T::DbWeight::get().reads(12))
.saturating_add(T::DbWeight::get().writes(8))
}
/// Storage: `Hrmp::HrmpChannels` (r:1 w:1)
/// Proof: `Hrmp::HrmpChannels` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn poke_channel_deposits() -> Weight {
// Proof Size summary in bytes:
// Measured: `263`
// Estimated: `3728`
// Minimum execution time: 173_371_000 picoseconds.
Weight::from_parts(175_860_000, 0)
.saturating_add(Weight::from_parts(0, 3728))
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1))
}
}
+194 -18
View File
@@ -21,13 +21,16 @@ use crate::{
use frame_support::{pallet_prelude::*, traits::ReservableCurrency, DefaultNoBound};
use frame_system::pallet_prelude::*;
use parity_scale_codec::{Decode, Encode};
use polkadot_parachain_primitives::primitives::HorizontalMessages;
use polkadot_parachain_primitives::primitives::{HorizontalMessages, IsSystem};
use primitives::{
Balance, Hash, HrmpChannelId, Id as ParaId, InboundHrmpMessage, OutboundHrmpMessage,
SessionIndex,
};
use scale_info::TypeInfo;
use sp_runtime::traits::{AccountIdConversion, BlakeTwo256, Hash as HashT, UniqueSaturatedInto};
use sp_runtime::{
traits::{AccountIdConversion, BlakeTwo256, Hash as HashT, UniqueSaturatedInto, Zero},
ArithmeticError,
};
use sp_std::{
collections::{btree_map::BTreeMap, btree_set::BTreeSet},
fmt, mem,
@@ -60,6 +63,8 @@ pub trait WeightInfo {
fn hrmp_cancel_open_request(c: u32) -> Weight;
fn clean_open_channel_requests(c: u32) -> Weight;
fn force_open_hrmp_channel(c: u32) -> Weight;
fn establish_system_channel() -> Weight;
fn poke_channel_deposits() -> Weight;
}
/// A weight info that is only suitable for testing.
@@ -93,6 +98,12 @@ impl WeightInfo for TestWeightInfo {
fn force_open_hrmp_channel(_: u32) -> Weight {
Weight::MAX
}
fn establish_system_channel() -> Weight {
Weight::MAX
}
fn poke_channel_deposits() -> Weight {
Weight::MAX
}
}
/// A description of a request to open an HRMP channel.
@@ -279,6 +290,12 @@ pub mod pallet {
/// An HRMP channel was opened via Root origin.
/// `[sender, recipient, proposed_max_capacity, proposed_max_message_size]`
HrmpChannelForceOpened(ParaId, ParaId, u32, u32),
/// An HRMP channel was opened between two system chains.
/// `[sender, recipient, proposed_max_capacity, proposed_max_message_size]`
HrmpSystemChannelOpened(ParaId, ParaId, u32, u32),
/// An HRMP channel's deposits were updated.
/// `[sender, recipient]`
OpenChannelDepositsUpdated(ParaId, ParaId),
}
#[pallet::error]
@@ -321,6 +338,8 @@ pub mod pallet {
OpenHrmpChannelAlreadyConfirmed,
/// The provided witness data is wrong.
WrongWitness,
/// The channel between these two chains cannot be authorized.
ChannelCreationNotAuthorized,
}
/// The set of pending HRMP open channel requests.
@@ -600,8 +619,8 @@ pub mod pallet {
/// 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 system parachain.
/// Expected use is when one (and only one) of the `ParaId`s involved in the channel is
/// governed by the system, e.g. a system parachain.
///
/// Origin must be the `ChannelManager`.
#[pallet::call_index(7)]
@@ -628,7 +647,8 @@ pub mod pallet {
0
};
// Now we proceed with normal init/accept.
// Now we proceed with normal init/accept, except that we set `no_deposit` to true such
// that it will not require deposits from either member.
Self::init_open_channel(sender, recipient, max_capacity, max_message_size)?;
Self::accept_open_channel(recipient, sender)?;
Self::deposit_event(Event::HrmpChannelForceOpened(
@@ -640,6 +660,146 @@ pub mod pallet {
Ok(Some(<T as Config>::WeightInfo::force_open_hrmp_channel(cancel_request)).into())
}
/// Establish an HRMP channel between two system chains. If the channel does not already
/// exist, the transaction fees will be refunded to the caller. The system does not take
/// deposits for channels between system chains, and automatically sets the message number
/// and size limits to the maximum allowed by the network's configuration.
///
/// Arguments:
///
/// - `sender`: A system chain, `ParaId`.
/// - `recipient`: A system chain, `ParaId`.
///
/// Any signed origin can call this function, but _both_ inputs MUST be system chains. If
/// the channel does not exist yet, there is no fee.
#[pallet::call_index(8)]
#[pallet::weight(<T as Config>::WeightInfo::establish_system_channel())]
pub fn establish_system_channel(
origin: OriginFor<T>,
sender: ParaId,
recipient: ParaId,
) -> DispatchResultWithPostInfo {
let _caller = ensure_signed(origin)?;
// both chains must be system
ensure!(
sender.is_system() && recipient.is_system(),
Error::<T>::ChannelCreationNotAuthorized
);
let config = <configuration::Pallet<T>>::config();
let max_message_size = config.hrmp_channel_max_message_size;
let max_capacity = config.hrmp_channel_max_capacity;
Self::init_open_channel(sender, recipient, max_capacity, max_message_size)?;
Self::accept_open_channel(recipient, sender)?;
Self::deposit_event(Event::HrmpSystemChannelOpened(
sender,
recipient,
max_capacity,
max_message_size,
));
Ok(Pays::No.into())
}
/// Update the deposits held for an HRMP channel to the latest `Configuration`. Channels
/// with system chains do not require a deposit.
///
/// Arguments:
///
/// - `sender`: A chain, `ParaId`.
/// - `recipient`: A chain, `ParaId`.
///
/// Any signed origin can call this function.
#[pallet::call_index(9)]
#[pallet::weight(<T as Config>::WeightInfo::poke_channel_deposits())]
pub fn poke_channel_deposits(
origin: OriginFor<T>,
sender: ParaId,
recipient: ParaId,
) -> DispatchResult {
let _caller = ensure_signed(origin)?;
let channel_id = HrmpChannelId { sender, recipient };
let is_system = sender.is_system() || recipient.is_system();
let config = <configuration::Pallet<T>>::config();
// Channels with and amongst the system do not require a deposit.
let (new_sender_deposit, new_recipient_deposit) = if is_system {
(0, 0)
} else {
(config.hrmp_sender_deposit, config.hrmp_recipient_deposit)
};
let _ = HrmpChannels::<T>::mutate(&channel_id, |channel| -> DispatchResult {
if let Some(ref mut channel) = channel {
let current_sender_deposit = channel.sender_deposit;
let current_recipient_deposit = channel.recipient_deposit;
// nothing to update
if current_sender_deposit == new_sender_deposit &&
current_recipient_deposit == new_recipient_deposit
{
return Ok(())
}
// sender
if current_sender_deposit > new_sender_deposit {
// Can never underflow, but be paranoid.
let amount = current_sender_deposit
.checked_sub(new_sender_deposit)
.ok_or(ArithmeticError::Underflow)?;
T::Currency::unreserve(
&channel_id.sender.into_account_truncating(),
// The difference should always be convertable into `Balance`, but be
// paranoid and do nothing in case.
amount.try_into().unwrap_or(Zero::zero()),
);
} else if current_sender_deposit < new_sender_deposit {
let amount = new_sender_deposit
.checked_sub(current_sender_deposit)
.ok_or(ArithmeticError::Underflow)?;
T::Currency::reserve(
&channel_id.sender.into_account_truncating(),
amount.try_into().unwrap_or(Zero::zero()),
)?;
}
// recipient
if current_recipient_deposit > new_recipient_deposit {
let amount = current_recipient_deposit
.checked_sub(new_recipient_deposit)
.ok_or(ArithmeticError::Underflow)?;
T::Currency::unreserve(
&channel_id.recipient.into_account_truncating(),
amount.try_into().unwrap_or(Zero::zero()),
);
} else if current_recipient_deposit < new_recipient_deposit {
let amount = new_recipient_deposit
.checked_sub(current_recipient_deposit)
.ok_or(ArithmeticError::Underflow)?;
T::Currency::reserve(
&channel_id.recipient.into_account_truncating(),
amount.try_into().unwrap_or(Zero::zero()),
)?;
}
// update storage
channel.sender_deposit = new_sender_deposit;
channel.recipient_deposit = new_recipient_deposit;
} else {
return Err(Error::<T>::OpenHrmpChannelDoesntExist.into())
}
Ok(())
})?;
Self::deposit_event(Event::OpenChannelDepositsUpdated(sender, recipient));
Ok(())
}
}
}
@@ -817,6 +977,10 @@ impl<T: Config> Pallet<T> {
"can't be `None` due to the invariant that the list contains the same items as the set; qed",
);
let system_channel = channel_id.sender.is_system() || channel_id.recipient.is_system();
let sender_deposit = request.sender_deposit;
let recipient_deposit = if system_channel { 0 } else { config.hrmp_recipient_deposit };
if request.confirmed {
if <paras::Pallet<T>>::is_valid_para(channel_id.sender) &&
<paras::Pallet<T>>::is_valid_para(channel_id.recipient)
@@ -824,8 +988,8 @@ impl<T: Config> Pallet<T> {
HrmpChannels::<T>::insert(
&channel_id,
HrmpChannel {
sender_deposit: request.sender_deposit,
recipient_deposit: config.hrmp_recipient_deposit,
sender_deposit,
recipient_deposit,
max_capacity: request.max_capacity,
max_total_size: request.max_total_size,
max_message_size: request.max_message_size,
@@ -1173,7 +1337,9 @@ impl<T: Config> Pallet<T> {
}
/// Initiate opening a channel from a parachain to a given recipient with given channel
/// parameters.
/// parameters. If neither chain is part of the system, then a deposit from the `Configuration`
/// will be required for `origin` (the sender) upon opening the request and the `recipient` upon
/// accepting it.
///
/// Basically the same as [`hrmp_init_open_channel`](Pallet::hrmp_init_open_channel) but
/// intended for calling directly from other pallets rather than dispatched.
@@ -1219,10 +1385,15 @@ impl<T: Config> Pallet<T> {
Error::<T>::OpenHrmpChannelLimitExceeded,
);
T::Currency::reserve(
&origin.into_account_truncating(),
config.hrmp_sender_deposit.unique_saturated_into(),
)?;
// Do not require deposits for channels with or amongst the system.
let is_system = origin.is_system() || recipient.is_system();
let deposit = if is_system { 0 } else { config.hrmp_sender_deposit };
if !deposit.is_zero() {
T::Currency::reserve(
&origin.into_account_truncating(),
deposit.unique_saturated_into(),
)?;
}
// mutating storage directly now -- shall not bail henceforth.
@@ -1232,7 +1403,7 @@ impl<T: Config> Pallet<T> {
HrmpOpenChannelRequest {
confirmed: false,
_age: 0,
sender_deposit: config.hrmp_sender_deposit,
sender_deposit: deposit,
max_capacity: proposed_max_capacity,
max_message_size: proposed_max_message_size,
max_total_size: config.hrmp_channel_max_total_size,
@@ -1254,7 +1425,7 @@ impl<T: Config> Pallet<T> {
if let Err(dmp::QueueDownwardMessageError::ExceedsMaxMessageSize) =
<dmp::Pallet<T>>::queue_downward_message(&config, recipient, notification_bytes)
{
// this should never happen unless the max downward message size is configured to an
// this should never happen unless the max downward message size is configured to a
// jokingly small number.
log::error!(
target: "runtime::hrmp",
@@ -1287,10 +1458,15 @@ impl<T: Config> Pallet<T> {
Error::<T>::AcceptHrmpChannelLimitExceeded,
);
T::Currency::reserve(
&origin.into_account_truncating(),
config.hrmp_recipient_deposit.unique_saturated_into(),
)?;
// Do not require deposits for channels with or amongst the system.
let is_system = origin.is_system() || sender.is_system();
let deposit = if is_system { 0 } else { config.hrmp_recipient_deposit };
if !deposit.is_zero() {
T::Currency::reserve(
&origin.into_account_truncating(),
deposit.unique_saturated_into(),
)?;
}
// persist the updated open channel request and then increment the number of accepted
// channels.
@@ -304,14 +304,13 @@ frame_benchmarking::benchmarks! {
let sender_origin: crate::Origin = 1u32.into();
let recipient_id: ParaId = 2u32.into();
// make sure para is registered, and has enough balance.
// Make sure para is registered. The sender does actually need the normal deposit because it
// is first going the "init" route.
let ed = T::Currency::minimum_balance();
let sender_deposit: BalanceOf<T> =
Configuration::<T>::config().hrmp_sender_deposit.unique_saturated_into();
let recipient_deposit: BalanceOf<T> =
Configuration::<T>::config().hrmp_recipient_deposit.unique_saturated_into();
register_parachain_with_balance::<T>(sender_id, sender_deposit + ed);
register_parachain_with_balance::<T>(recipient_id, recipient_deposit + ed);
register_parachain_with_balance::<T>(recipient_id, Zero::zero());
let capacity = Configuration::<T>::config().hrmp_channel_max_capacity;
let message_size = Configuration::<T>::config().hrmp_channel_max_message_size;
@@ -351,6 +350,72 @@ frame_benchmarking::benchmarks! {
Event::<T>::HrmpChannelForceOpened(sender_id, recipient_id, capacity, message_size).into()
);
}
establish_system_channel {
let sender_id: ParaId = 1u32.into();
let recipient_id: ParaId = 2u32.into();
let caller: T::AccountId = frame_benchmarking::whitelisted_caller();
let config = Configuration::<T>::config();
// make sure para is registered, and has zero balance.
register_parachain_with_balance::<T>(sender_id, Zero::zero());
register_parachain_with_balance::<T>(recipient_id, Zero::zero());
let capacity = config.hrmp_channel_max_capacity;
let message_size = config.hrmp_channel_max_message_size;
}: _(frame_system::RawOrigin::Signed(caller), sender_id, recipient_id)
verify {
assert_last_event::<T>(
Event::<T>::HrmpSystemChannelOpened(sender_id, recipient_id, capacity, message_size).into()
);
}
poke_channel_deposits {
let sender_id: ParaId = 1u32.into();
let recipient_id: ParaId = 2u32.into();
let channel_id = HrmpChannelId {sender: sender_id, recipient: recipient_id };
let caller: T::AccountId = frame_benchmarking::whitelisted_caller();
let config = Configuration::<T>::config();
// make sure para is registered, and has balance to reserve.
let ed = T::Currency::minimum_balance();
let sender_deposit: BalanceOf<T> = config.hrmp_sender_deposit.unique_saturated_into();
let recipient_deposit: BalanceOf<T> = config.hrmp_recipient_deposit.unique_saturated_into();
register_parachain_with_balance::<T>(sender_id, ed + sender_deposit);
register_parachain_with_balance::<T>(recipient_id, ed + recipient_deposit);
// Our normal establishment won't actually reserve deposits, so just insert them directly.
HrmpChannels::<T>::insert(
&channel_id,
HrmpChannel {
sender_deposit: config.hrmp_sender_deposit,
recipient_deposit: config.hrmp_recipient_deposit,
max_capacity: config.hrmp_channel_max_capacity,
max_total_size: config.hrmp_channel_max_total_size,
max_message_size: config.hrmp_channel_max_message_size,
msg_count: 0,
total_size: 0,
mqc_head: None,
},
);
// Actually reserve the deposits.
let _ = T::Currency::reserve(&sender_id.into_account_truncating(), sender_deposit);
let _ = T::Currency::reserve(&recipient_id.into_account_truncating(), recipient_deposit);
}: _(frame_system::RawOrigin::Signed(caller), sender_id, recipient_id)
verify {
assert_last_event::<T>(
Event::<T>::OpenChannelDepositsUpdated(sender_id, recipient_id).into()
);
let channel = HrmpChannels::<T>::get(&channel_id).unwrap();
// Check that the deposit was updated in the channel state.
assert_eq!(channel.sender_deposit, 0);
assert_eq!(channel.recipient_deposit, 0);
// And that the funds were unreserved.
assert_eq!(T::Currency::reserved_balance(&sender_id.into_account_truncating()), 0u128.unique_saturated_into());
assert_eq!(T::Currency::reserved_balance(&recipient_id.into_account_truncating()), 0u128.unique_saturated_into());
}
}
frame_benchmarking::impl_benchmark_test_suite!(
+161 -33
View File
@@ -14,13 +14,17 @@
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
// NOTE: System chains, identified by ParaId < 2000, are treated as special in HRMP channel
// initialization. Namely, they do not require a deposit if even one ParaId is a system para. If
// both paras are system chains, then they are also configured to the system's max configuration.
use super::*;
use crate::mock::{
deregister_parachain, new_test_ext, register_parachain, register_parachain_with_balance,
Configuration, Hrmp, MockGenesisConfig, Paras, ParasShared, RuntimeEvent as MockEvent,
RuntimeOrigin, System, Test,
};
use frame_support::assert_noop;
use frame_support::{assert_noop, assert_ok};
use primitives::BlockNumber;
use std::collections::BTreeMap;
@@ -133,10 +137,10 @@ fn empty_state_consistent_state() {
#[test]
fn open_channel_works() {
let para_a = 1.into();
let para_a_origin: crate::Origin = 1.into();
let para_b = 3.into();
let para_b_origin: crate::Origin = 3.into();
let para_a = 2001.into();
let para_a_origin: crate::Origin = 2001.into();
let para_b = 2003.into();
let para_b_origin: crate::Origin = 2003.into();
new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| {
// We need both A & B to be registered and alive parachains.
@@ -171,13 +175,16 @@ fn open_channel_works() {
#[test]
fn force_open_channel_works() {
let para_a = 1.into();
let para_b = 3.into();
let para_b = 2003.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);
let para_b_free_balance =
<Test as Config>::Currency::free_balance(&para_b.into_account_truncating());
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();
@@ -193,14 +200,19 @@ fn force_open_channel_works() {
// 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));
// Because para_a is a system chain, para_b's free balance should not have changed.
assert_eq!(
<Test as Config>::Currency::free_balance(&para_b.into_account_truncating()),
para_b_free_balance
);
});
}
#[test]
fn force_open_channel_works_with_existing_request() {
let para_a = 1.into();
let para_a_origin: crate::Origin = 1.into();
let para_b = 3.into();
let para_a = 2001.into();
let para_a_origin: crate::Origin = 2001.into();
let para_b = 2003.into();
new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| {
// We need both A & B to be registered and live parachains.
@@ -240,11 +252,127 @@ fn force_open_channel_works_with_existing_request() {
});
}
#[test]
fn open_system_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::establish_system_channel(RuntimeOrigin::signed(1), para_a, para_b).unwrap();
Hrmp::assert_storage_consistency_exhaustive();
assert!(System::events().iter().any(|record| record.event ==
MockEvent::Hrmp(Event::HrmpSystemChannelOpened(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 open_system_channel_does_not_work_for_non_system_chains() {
let para_a = 2001.into();
let para_b = 2003.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]));
assert_noop!(
Hrmp::establish_system_channel(RuntimeOrigin::signed(1), para_a, para_b),
Error::<Test>::ChannelCreationNotAuthorized
);
Hrmp::assert_storage_consistency_exhaustive();
});
}
#[test]
fn open_system_channel_does_not_work_with_one_non_system_chain() {
let para_a = 1.into();
let para_b = 2003.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]));
assert_noop!(
Hrmp::establish_system_channel(RuntimeOrigin::signed(1), para_a, para_b),
Error::<Test>::ChannelCreationNotAuthorized
);
Hrmp::assert_storage_consistency_exhaustive();
});
}
#[test]
fn poke_deposits_works() {
let para_a = 1.into();
let para_b = 2001.into();
new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| {
// We need both A & B to be registered and live parachains.
register_parachain_with_balance(para_a, 200);
register_parachain_with_balance(para_b, 200);
let config = Configuration::config();
let channel_id = HrmpChannelId { sender: para_a, recipient: para_b };
// Our normal establishment won't actually reserve deposits, so just insert them directly.
HrmpChannels::<Test>::insert(
&channel_id,
HrmpChannel {
sender_deposit: config.hrmp_sender_deposit,
recipient_deposit: config.hrmp_recipient_deposit,
max_capacity: config.hrmp_channel_max_capacity,
max_total_size: config.hrmp_channel_max_total_size,
max_message_size: config.hrmp_channel_max_message_size,
msg_count: 0,
total_size: 0,
mqc_head: None,
},
);
// reserve funds
assert_ok!(<Test as Config>::Currency::reserve(
&para_a.into_account_truncating(),
config.hrmp_sender_deposit
));
assert_ok!(<Test as Config>::Currency::reserve(
&para_b.into_account_truncating(),
config.hrmp_recipient_deposit
));
assert_ok!(Hrmp::poke_channel_deposits(RuntimeOrigin::signed(1), para_a, para_b));
assert_eq!(
<Test as Config>::Currency::reserved_balance(&para_a.into_account_truncating()),
0
);
assert_eq!(
<Test as Config>::Currency::reserved_balance(&para_b.into_account_truncating()),
0
);
});
}
#[test]
fn close_channel_works() {
let para_a = 5.into();
let para_b = 2.into();
let para_b_origin: crate::Origin = 2.into();
let para_a = 2005.into();
let para_b = 2002.into();
let para_b_origin: crate::Origin = 2002.into();
new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| {
register_parachain(para_a);
@@ -275,8 +403,8 @@ fn close_channel_works() {
#[test]
fn send_recv_messages() {
let para_a = 32.into();
let para_b = 64.into();
let para_a = 2032.into();
let para_b = 2064.into();
let mut genesis = GenesisConfigBuilder::default();
genesis.hrmp_channel_max_message_size = 20;
@@ -358,8 +486,8 @@ fn hrmp_mqc_head_fixture() {
#[test]
fn accept_incoming_request_and_offboard() {
let para_a = 32.into();
let para_b = 64.into();
let para_a = 2032.into();
let para_b = 2064.into();
new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| {
register_parachain(para_a);
@@ -380,9 +508,9 @@ fn accept_incoming_request_and_offboard() {
#[test]
fn check_sent_messages() {
let para_a = 32.into();
let para_b = 64.into();
let para_c = 97.into();
let para_a = 2032.into();
let para_b = 2064.into();
let para_c = 2097.into();
new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| {
register_parachain(para_a);
@@ -444,8 +572,8 @@ fn check_sent_messages() {
fn verify_externally_accessible() {
use primitives::{well_known_keys, AbridgedHrmpChannel};
let para_a = 20.into();
let para_b = 21.into();
let para_a = 2020.into();
let para_b = 2021.into();
new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| {
// Register two parachains, wait until a session change, then initiate channel open
@@ -502,8 +630,8 @@ fn verify_externally_accessible() {
#[test]
fn charging_deposits() {
let para_a = 32.into();
let para_b = 64.into();
let para_a = 2032.into();
let para_b = 2064.into();
new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| {
register_parachain_with_balance(para_a, 0);
@@ -532,8 +660,8 @@ fn charging_deposits() {
#[test]
fn refund_deposit_on_normal_closure() {
let para_a = 32.into();
let para_b = 64.into();
let para_a = 2032.into();
let para_b = 2064.into();
let mut genesis = GenesisConfigBuilder::default();
genesis.hrmp_sender_deposit = 20;
@@ -565,8 +693,8 @@ fn refund_deposit_on_normal_closure() {
#[test]
fn refund_deposit_on_offboarding() {
let para_a = 32.into();
let para_b = 64.into();
let para_a = 2032.into();
let para_b = 2064.into();
let mut genesis = GenesisConfigBuilder::default();
genesis.hrmp_sender_deposit = 20;
@@ -605,8 +733,8 @@ fn refund_deposit_on_offboarding() {
#[test]
fn no_dangling_open_requests() {
let para_a = 32.into();
let para_b = 64.into();
let para_a = 2032.into();
let para_b = 2064.into();
let mut genesis = GenesisConfigBuilder::default();
genesis.hrmp_sender_deposit = 20;
@@ -643,8 +771,8 @@ fn no_dangling_open_requests() {
#[test]
fn cancel_pending_open_channel_request() {
let para_a = 32.into();
let para_b = 64.into();
let para_a = 2032.into();
let para_b = 2064.into();
let mut genesis = GenesisConfigBuilder::default();
genesis.hrmp_sender_deposit = 20;
@@ -675,8 +803,8 @@ fn cancel_pending_open_channel_request() {
#[test]
fn watermark_maxed_out_at_relay_parent() {
let para_a = 32.into();
let para_b = 64.into();
let para_a = 2032.into();
let para_b = 2064.into();
let mut genesis = GenesisConfigBuilder::default();
genesis.hrmp_channel_max_message_size = 20;
@@ -292,4 +292,46 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
.saturating_add(T::DbWeight::get().reads(14))
.saturating_add(T::DbWeight::get().writes(8))
}
/// Storage: `Paras::ParaLifecycles` (r:1 w:0)
/// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Hrmp::HrmpOpenChannelRequests` (r:1 w:1)
/// Proof: `Hrmp::HrmpOpenChannelRequests` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Hrmp::HrmpChannels` (r:1 w:0)
/// Proof: `Hrmp::HrmpChannels` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Hrmp::HrmpEgressChannelsIndex` (r:1 w:0)
/// Proof: `Hrmp::HrmpEgressChannelsIndex` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Hrmp::HrmpOpenChannelRequestCount` (r:1 w:1)
/// Proof: `Hrmp::HrmpOpenChannelRequestCount` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Hrmp::HrmpOpenChannelRequestsList` (r:1 w:1)
/// Proof: `Hrmp::HrmpOpenChannelRequestsList` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
/// Storage: `Dmp::DownwardMessageQueues` (r:2 w:2)
/// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Dmp::DownwardMessageQueueHeads` (r:2 w:2)
/// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Hrmp::HrmpIngressChannelsIndex` (r:1 w:0)
/// Proof: `Hrmp::HrmpIngressChannelsIndex` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Hrmp::HrmpAcceptedChannelRequestCount` (r:1 w:1)
/// Proof: `Hrmp::HrmpAcceptedChannelRequestCount` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn establish_system_channel() -> Weight {
// Proof Size summary in bytes:
// Measured: `417`
// Estimated: `6357`
// Minimum execution time: 629_674_000 picoseconds.
Weight::from_parts(640_174_000, 0)
.saturating_add(Weight::from_parts(0, 6357))
.saturating_add(T::DbWeight::get().reads(12))
.saturating_add(T::DbWeight::get().writes(8))
}
/// Storage: `Hrmp::HrmpChannels` (r:1 w:1)
/// Proof: `Hrmp::HrmpChannels` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn poke_channel_deposits() -> Weight {
// Proof Size summary in bytes:
// Measured: `263`
// Estimated: `3728`
// Minimum execution time: 173_371_000 picoseconds.
Weight::from_parts(175_860_000, 0)
.saturating_add(Weight::from_parts(0, 3728))
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1))
}
}
@@ -289,4 +289,46 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
.saturating_add(T::DbWeight::get().reads(14))
.saturating_add(T::DbWeight::get().writes(8))
}
/// Storage: `Paras::ParaLifecycles` (r:1 w:0)
/// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Hrmp::HrmpOpenChannelRequests` (r:1 w:1)
/// Proof: `Hrmp::HrmpOpenChannelRequests` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Hrmp::HrmpChannels` (r:1 w:0)
/// Proof: `Hrmp::HrmpChannels` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Hrmp::HrmpEgressChannelsIndex` (r:1 w:0)
/// Proof: `Hrmp::HrmpEgressChannelsIndex` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Hrmp::HrmpOpenChannelRequestCount` (r:1 w:1)
/// Proof: `Hrmp::HrmpOpenChannelRequestCount` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Hrmp::HrmpOpenChannelRequestsList` (r:1 w:1)
/// Proof: `Hrmp::HrmpOpenChannelRequestsList` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
/// Storage: `Dmp::DownwardMessageQueues` (r:2 w:2)
/// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Dmp::DownwardMessageQueueHeads` (r:2 w:2)
/// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Hrmp::HrmpIngressChannelsIndex` (r:1 w:0)
/// Proof: `Hrmp::HrmpIngressChannelsIndex` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Hrmp::HrmpAcceptedChannelRequestCount` (r:1 w:1)
/// Proof: `Hrmp::HrmpAcceptedChannelRequestCount` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn establish_system_channel() -> Weight {
// Proof Size summary in bytes:
// Measured: `417`
// Estimated: `6357`
// Minimum execution time: 629_674_000 picoseconds.
Weight::from_parts(640_174_000, 0)
.saturating_add(Weight::from_parts(0, 6357))
.saturating_add(T::DbWeight::get().reads(12))
.saturating_add(T::DbWeight::get().writes(8))
}
/// Storage: `Hrmp::HrmpChannels` (r:1 w:1)
/// Proof: `Hrmp::HrmpChannels` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn poke_channel_deposits() -> Weight {
// Proof Size summary in bytes:
// Measured: `263`
// Estimated: `3728`
// Minimum execution time: 173_371_000 picoseconds.
Weight::from_parts(175_860_000, 0)
.saturating_add(Weight::from_parts(0, 3728))
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1))
}
}
@@ -282,4 +282,46 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf
.saturating_add(T::DbWeight::get().reads(13))
.saturating_add(T::DbWeight::get().writes(8))
}
/// Storage: `Paras::ParaLifecycles` (r:1 w:0)
/// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Hrmp::HrmpOpenChannelRequests` (r:1 w:1)
/// Proof: `Hrmp::HrmpOpenChannelRequests` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Hrmp::HrmpChannels` (r:1 w:0)
/// Proof: `Hrmp::HrmpChannels` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Hrmp::HrmpEgressChannelsIndex` (r:1 w:0)
/// Proof: `Hrmp::HrmpEgressChannelsIndex` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Hrmp::HrmpOpenChannelRequestCount` (r:1 w:1)
/// Proof: `Hrmp::HrmpOpenChannelRequestCount` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Hrmp::HrmpOpenChannelRequestsList` (r:1 w:1)
/// Proof: `Hrmp::HrmpOpenChannelRequestsList` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
/// Storage: `Dmp::DownwardMessageQueues` (r:2 w:2)
/// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Dmp::DownwardMessageQueueHeads` (r:2 w:2)
/// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Hrmp::HrmpIngressChannelsIndex` (r:1 w:0)
/// Proof: `Hrmp::HrmpIngressChannelsIndex` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Hrmp::HrmpAcceptedChannelRequestCount` (r:1 w:1)
/// Proof: `Hrmp::HrmpAcceptedChannelRequestCount` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn establish_system_channel() -> Weight {
// Proof Size summary in bytes:
// Measured: `417`
// Estimated: `6357`
// Minimum execution time: 629_674_000 picoseconds.
Weight::from_parts(640_174_000, 0)
.saturating_add(Weight::from_parts(0, 6357))
.saturating_add(T::DbWeight::get().reads(12))
.saturating_add(T::DbWeight::get().writes(8))
}
/// Storage: `Hrmp::HrmpChannels` (r:1 w:1)
/// Proof: `Hrmp::HrmpChannels` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn poke_channel_deposits() -> Weight {
// Proof Size summary in bytes:
// Measured: `263`
// Estimated: `3728`
// Minimum execution time: 173_371_000 picoseconds.
Weight::from_parts(175_860_000, 0)
.saturating_add(Weight::from_parts(0, 3728))
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1))
}
}