mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 08:41:02 +00:00
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:
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user