mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-28 21:27:57 +00:00
Do not expire HRMP open channel requests (#3543)
* Do not expire HRMP open channel requests * Fix the build and update the docs * Implement canceling requests and do not remove them automatically * Fix a borked merge * Fix fmt * Please spellchecker * Apply suggestions from code review Co-authored-by: Amar Singh <asinghchrony@protonmail.com> * Use `mutate_exists` for maintaining request counts * Apply `rustfmt` * Move newly introduced entrypoint to end to preserve ordering Co-authored-by: Amar Singh <asinghchrony@protonmail.com>
This commit is contained in:
@@ -174,7 +174,7 @@ fn default_parachains_host_configuration(
|
||||
ump_service_total_weight: 4 * 1_000_000_000,
|
||||
max_upward_message_size: 1024 * 1024,
|
||||
max_upward_message_num_per_candidate: 5,
|
||||
hrmp_open_request_ttl: 5,
|
||||
_hrmp_open_request_ttl: 5,
|
||||
hrmp_sender_deposit: 0,
|
||||
hrmp_recipient_deposit: 0,
|
||||
hrmp_channel_max_capacity: 8,
|
||||
|
||||
@@ -328,7 +328,8 @@ impl<T: Encode + Decode + Default> AccountIdConversion<T> for Id {
|
||||
/// unidirectional, meaning that `(A, B)` and `(B, A)` refer to different channels. The convention is
|
||||
/// that we use the first item tuple for the sender and the second for the recipient. Only one channel
|
||||
/// is allowed between two participants in one direction, i.e. there cannot be 2 different channels
|
||||
/// identified by `(A, B)`.
|
||||
/// identified by `(A, B)`. A channel with the same para id in sender and recipient is invalid. That
|
||||
/// is, however, not enforced.
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Encode, Decode, RuntimeDebug)]
|
||||
#[cfg_attr(feature = "std", derive(Hash))]
|
||||
pub struct HrmpChannelId {
|
||||
@@ -338,6 +339,13 @@ pub struct HrmpChannelId {
|
||||
pub recipient: Id,
|
||||
}
|
||||
|
||||
impl HrmpChannelId {
|
||||
/// Returns true if the given id corresponds to either the sender or the recipient.
|
||||
pub fn is_participant(&self, id: Id) -> bool {
|
||||
id == self.sender || id == self.recipient
|
||||
}
|
||||
}
|
||||
|
||||
/// A message from a parachain to its Relay Chain.
|
||||
pub type UpwardMessage = Vec<u8>;
|
||||
|
||||
|
||||
@@ -11,8 +11,6 @@ HRMP related structs:
|
||||
struct HrmpOpenChannelRequest {
|
||||
/// Indicates if this request was confirmed by the recipient.
|
||||
confirmed: bool,
|
||||
/// How many session boundaries ago this request was seen.
|
||||
age: SessionIndex,
|
||||
/// The amount that the sender supplied at the time of creation of this request.
|
||||
sender_deposit: Balance,
|
||||
/// The maximum message size that could be put into the channel.
|
||||
@@ -158,8 +156,8 @@ Candidate Enactment:
|
||||
1. Decrement `C.msg_count`
|
||||
1. Decrement `C.total_size` by `M`'s payload size.
|
||||
1. Set `HrmpWatermarks` for `P` to be equal to `new_hrmp_watermark`
|
||||
> NOTE: That collecting digests can be inefficient and the time it takes grows very fast. Thanks to the aggresive
|
||||
> parametrization this shouldn't be a big of a deal.
|
||||
> NOTE: That collecting digests can be inefficient and the time it takes grows very fast. Thanks to the aggressive
|
||||
> parameterization this shouldn't be a big of a deal.
|
||||
> If that becomes a problem consider introducing an extra dictionary which says at what block the given sender
|
||||
> sent a message to the recipient.
|
||||
|
||||
@@ -212,6 +210,13 @@ the parachain executed the message.
|
||||
- The DM is sent using `queue_downward_message`.
|
||||
- The DM is represented by the `HrmpChannelAccepted` XCM message.
|
||||
- `recipient` is set to `origin`.
|
||||
* `hrmp_cancel_open_request(ch)`:
|
||||
1. Check that `origin` is either `ch.sender` or `ch.recipient`
|
||||
1. Check that the open channel request `ch` exists.
|
||||
1. Check that the open channel request for `ch` is not confirmed.
|
||||
1. Remove `ch` from `HrmpOpenChannelRequests` and `HrmpOpenChannelRequestsList`
|
||||
1. Decrement `HrmpAcceptedChannelRequestCount` for `ch.recipient` by 1.
|
||||
1. Unreserve the deposit of `ch.sender`.
|
||||
* `hrmp_close_channel(ch)`:
|
||||
1. Check that `origin` is either `ch.sender` or `ch.recipient`
|
||||
1. Check that `HrmpChannels` for `ch` exists.
|
||||
@@ -233,15 +238,12 @@ the parachain executed the message.
|
||||
1. Remove all outbound channels of `P`, i.e. `(P, _)`,
|
||||
1. Remove `HrmpOpenChannelRequestCount` for `P`
|
||||
1. Remove `HrmpAcceptedChannelRequestCount` for `P`.
|
||||
1. Remove `HrmpOpenChannelRequests` and `HrmpOpenChannelRequestsList` for `(P, _)` and `(_, P)`.
|
||||
1. For each removed channel request `C`:
|
||||
1. Unreserve the sender's deposit if the sender is not present in `outgoing_paras`
|
||||
1. Unreserve the recipient's deposit if `C` is confirmed and the recipient is not present in `outgoing_paras`
|
||||
1. For each channel designator `D` in `HrmpOpenChannelRequestsList` we query the request `R` from `HrmpOpenChannelRequests`:
|
||||
1. if `R.confirmed = false`:
|
||||
1. increment `R.age` by 1.
|
||||
1. if `R.age` reached a preconfigured time-to-live limit `config.hrmp_open_request_ttl`, then:
|
||||
1. refund `R.sender_deposit` to the sender
|
||||
1. decrement `HrmpOpenChannelRequestCount` for `D.sender` by 1.
|
||||
1. remove `R`
|
||||
1. remove `D`
|
||||
2. if `R.confirmed = true`,
|
||||
1. if `R.confirmed = true`,
|
||||
1. if both `D.sender` and `D.recipient` are not offboarded.
|
||||
1. create a new channel `C` between `(D.sender, D.recipient)`.
|
||||
1. Initialize the `C.sender_deposit` with `R.sender_deposit` and `C.recipient_deposit`
|
||||
|
||||
@@ -42,7 +42,7 @@ struct HostConfiguration {
|
||||
pub dispute_period: SessionIndex,
|
||||
/// How long after dispute conclusion to accept statements.
|
||||
pub dispute_post_conclusion_acceptance_period: BlockNumber,
|
||||
/// The maximum number of dispute spam slots
|
||||
/// The maximum number of dispute spam slots
|
||||
pub dispute_max_spam_slots: u32,
|
||||
/// How long it takes for a dispute to conclude by time-out, if no supermajority is reached.
|
||||
pub dispute_conclusion_by_time_out_period: BlockNumber,
|
||||
@@ -85,8 +85,6 @@ struct HostConfiguration {
|
||||
/// decide to do with its PoV so this value in practice will be picked as a fraction of the PoV
|
||||
/// size.
|
||||
pub max_downward_message_size: u32,
|
||||
/// Number of sessions after which an HRMP open channel request expires.
|
||||
pub hrmp_open_request_ttl: u32,
|
||||
/// The deposit that the sender should provide for opening an HRMP channel.
|
||||
pub hrmp_sender_deposit: u32,
|
||||
/// The deposit that the recipient should provide for accepting opening an HRMP channel.
|
||||
@@ -119,7 +117,7 @@ struct HostConfiguration {
|
||||
Inherent data passed to a runtime entry-point for the advancement of parachain consensus.
|
||||
|
||||
This contains 3 pieces of data:
|
||||
1. [`Bitfields`](availability.md#signed-availability-bitfield)
|
||||
1. [`Bitfields`](availability.md#signed-availability-bitfield)
|
||||
2. [`BackedCandidates`](backing.md#backed-candidate)
|
||||
3. [`MultiDisputeStatementSet`](disputes.md#multidisputestatementset)
|
||||
|
||||
|
||||
@@ -91,8 +91,10 @@ pub struct HostConfiguration<BlockNumber> {
|
||||
pub hrmp_max_parachain_outbound_channels: u32,
|
||||
/// The maximum number of outbound HRMP channels a parathread is allowed to open.
|
||||
pub hrmp_max_parathread_outbound_channels: u32,
|
||||
/// Number of sessions after which an HRMP open channel request expires.
|
||||
pub hrmp_open_request_ttl: u32,
|
||||
/// NOTE: this field is deprecated. Channel open requests became non-expiring. Changing this value
|
||||
/// doesn't have any effect. This field doesn't have a `deprecated` attribute because that would
|
||||
/// trigger warnings coming from macros.
|
||||
pub _hrmp_open_request_ttl: u32,
|
||||
/// The deposit that the sender should provide for opening an HRMP channel.
|
||||
pub hrmp_sender_deposit: Balance,
|
||||
/// The deposit that the recipient should provide for accepting opening an HRMP channel.
|
||||
@@ -202,7 +204,7 @@ impl<BlockNumber: Default + From<u32>> Default for HostConfiguration<BlockNumber
|
||||
ump_service_total_weight: Default::default(),
|
||||
max_upward_message_size: Default::default(),
|
||||
max_upward_message_num_per_candidate: Default::default(),
|
||||
hrmp_open_request_ttl: Default::default(),
|
||||
_hrmp_open_request_ttl: Default::default(),
|
||||
hrmp_sender_deposit: Default::default(),
|
||||
hrmp_recipient_deposit: Default::default(),
|
||||
hrmp_channel_max_capacity: Default::default(),
|
||||
@@ -641,12 +643,10 @@ pub mod pallet {
|
||||
|
||||
/// Sets the number of sessions after which an HRMP open channel request expires.
|
||||
#[pallet::weight((1_000, DispatchClass::Operational))]
|
||||
pub fn set_hrmp_open_request_ttl(origin: OriginFor<T>, new: u32) -> DispatchResult {
|
||||
ensure_root(origin)?;
|
||||
Self::update_config_member(|config| {
|
||||
sp_std::mem::replace(&mut config.hrmp_open_request_ttl, new) != new
|
||||
});
|
||||
Ok(())
|
||||
// Deprecated, but is not marked as such, because that would trigger warnings coming from
|
||||
// the macro.
|
||||
pub fn set_hrmp_open_request_ttl(_origin: OriginFor<T>, _new: u32) -> DispatchResult {
|
||||
Err("this doesn't have any effect".into())
|
||||
}
|
||||
|
||||
/// Sets the amount of funds that the sender should provide for opening an HRMP channel.
|
||||
@@ -888,7 +888,7 @@ mod tests {
|
||||
ump_service_total_weight: 20000,
|
||||
max_upward_message_size: 448,
|
||||
max_upward_message_num_per_candidate: 5,
|
||||
hrmp_open_request_ttl: 1312,
|
||||
_hrmp_open_request_ttl: 0,
|
||||
hrmp_sender_deposit: 22,
|
||||
hrmp_recipient_deposit: 4905,
|
||||
hrmp_channel_max_capacity: 3921,
|
||||
@@ -1013,11 +1013,6 @@ mod tests {
|
||||
new_config.max_upward_message_num_per_candidate,
|
||||
)
|
||||
.unwrap();
|
||||
Configuration::set_hrmp_open_request_ttl(
|
||||
Origin::root(),
|
||||
new_config.hrmp_open_request_ttl,
|
||||
)
|
||||
.unwrap();
|
||||
Configuration::set_hrmp_sender_deposit(Origin::root(), new_config.hrmp_sender_deposit)
|
||||
.unwrap();
|
||||
Configuration::set_hrmp_recipient_deposit(
|
||||
|
||||
@@ -39,8 +39,9 @@ pub use pallet::*;
|
||||
pub struct HrmpOpenChannelRequest {
|
||||
/// Indicates if this request was confirmed by the recipient.
|
||||
pub confirmed: bool,
|
||||
/// How many session boundaries ago this request was seen.
|
||||
pub age: SessionIndex,
|
||||
/// NOTE: this field is deprecated. Channel open requests became non-expiring and this value
|
||||
/// became unused.
|
||||
pub _age: SessionIndex,
|
||||
/// The amount that the sender supplied at the time of creation of this request.
|
||||
pub sender_deposit: Balance,
|
||||
/// The maximum message size that could be put into the channel.
|
||||
@@ -200,6 +201,9 @@ pub mod pallet {
|
||||
/// Open HRMP channel requested.
|
||||
/// `[sender, recipient, proposed_max_capacity, proposed_max_message_size]`
|
||||
OpenChannelRequested(ParaId, ParaId, u32, u32),
|
||||
/// An HRMP channel request sent by the receiver was canceled by either party.
|
||||
/// `[by_parachain, channel_id]`
|
||||
OpenChannelCanceled(ParaId, HrmpChannelId),
|
||||
/// Open HRMP channel accepted. `[sender, recipient]`
|
||||
OpenChannelAccepted(ParaId, ParaId),
|
||||
/// HRMP channel closed. `[by_parachain, channel_id]`
|
||||
@@ -238,6 +242,12 @@ pub mod pallet {
|
||||
CloseHrmpChannelDoesntExist,
|
||||
/// The channel close request is already requested.
|
||||
CloseHrmpChannelAlreadyUnderway,
|
||||
/// Canceling is requested by neither the sender nor recipient of the open channel request.
|
||||
CancelHrmpOpenChannelUnauthorized,
|
||||
/// The open request doesn't exist.
|
||||
OpenHrmpChannelDoesntExist,
|
||||
/// Cannot cancel an HRMP open channel request because it is already confirmed.
|
||||
OpenHrmpChannelAlreadyConfirmed,
|
||||
}
|
||||
|
||||
/// The set of pending HRMP open channel requests.
|
||||
@@ -464,6 +474,22 @@ pub mod pallet {
|
||||
Self::process_hrmp_close_channel_requests();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// This cancels a pending open channel request. It can be canceled be either of the sender
|
||||
/// or the recipient for that request. The origin must be either of those.
|
||||
///
|
||||
/// The cancelling happens immediately. It is not possible to cancel the request if it is
|
||||
/// already accepted.
|
||||
#[pallet::weight(0)]
|
||||
pub fn hrmp_cancel_open_request(
|
||||
origin: OriginFor<T>,
|
||||
channel_id: HrmpChannelId,
|
||||
) -> DispatchResult {
|
||||
let origin = ensure_parachain(<T as Config>::Origin::from(origin))?;
|
||||
Self::cancel_open_request(origin, channel_id.clone())?;
|
||||
Self::deposit_event(Event::OpenChannelCanceled(origin, channel_id));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -507,19 +533,77 @@ impl<T: Config> Pallet<T> {
|
||||
notification: &initializer::SessionChangeNotification<T::BlockNumber>,
|
||||
outgoing_paras: &[ParaId],
|
||||
) {
|
||||
Self::perform_outgoing_para_cleanup(outgoing_paras);
|
||||
Self::perform_outgoing_para_cleanup(¬ification.prev_config, outgoing_paras);
|
||||
Self::process_hrmp_open_channel_requests(¬ification.prev_config);
|
||||
Self::process_hrmp_close_channel_requests();
|
||||
}
|
||||
|
||||
/// Iterate over all paras that were noted for offboarding and remove all the data
|
||||
/// associated with them.
|
||||
fn perform_outgoing_para_cleanup(outgoing: &[ParaId]) {
|
||||
fn perform_outgoing_para_cleanup(
|
||||
config: &HostConfiguration<T::BlockNumber>,
|
||||
outgoing: &[ParaId],
|
||||
) {
|
||||
Self::clean_open_channel_requests(config, outgoing);
|
||||
for outgoing_para in outgoing {
|
||||
Self::clean_hrmp_after_outgoing(outgoing_para);
|
||||
}
|
||||
}
|
||||
|
||||
// Go over the HRMP open channel requests and remove all in which offboarding paras participate.
|
||||
//
|
||||
// This will also perform the refunds for the counterparty if it doesn't offboard.
|
||||
fn clean_open_channel_requests(
|
||||
config: &HostConfiguration<T::BlockNumber>,
|
||||
outgoing: &[ParaId],
|
||||
) {
|
||||
// First collect all the channel ids of the open requests in which there is at least one
|
||||
// party presents in the outgoing list.
|
||||
//
|
||||
// Both the open channel request list and outgoing list are expected to be small enough.
|
||||
// In the most common case there will be only single outgoing para.
|
||||
let open_channel_reqs = <Self as Store>::HrmpOpenChannelRequestsList::get();
|
||||
let (go, stay): (Vec<HrmpChannelId>, Vec<HrmpChannelId>) = open_channel_reqs
|
||||
.into_iter()
|
||||
.partition(|req_id| outgoing.iter().any(|id| req_id.is_participant(*id)));
|
||||
<Self as Store>::HrmpOpenChannelRequestsList::put(stay);
|
||||
|
||||
// Then iterate over all open requests to be removed, pull them out of the set and perform
|
||||
// the refunds if applicable.
|
||||
for req_id in go {
|
||||
let req_data = match <Self as Store>::HrmpOpenChannelRequests::take(&req_id) {
|
||||
Some(req_data) => req_data,
|
||||
None => {
|
||||
// Can't normally happen but no need to panic.
|
||||
continue
|
||||
},
|
||||
};
|
||||
|
||||
// Return the deposit of the sender, but only if it is not the para being offboarded.
|
||||
if !outgoing.contains(&req_id.sender) {
|
||||
T::Currency::unreserve(
|
||||
&req_id.sender.into_account(),
|
||||
req_data.sender_deposit.unique_saturated_into(),
|
||||
);
|
||||
}
|
||||
|
||||
// If the request was confirmed, then it means it was confirmed in the finished session.
|
||||
// Therefore, the config's hrmp_recipient_deposit represents the actual value of the
|
||||
// deposit.
|
||||
//
|
||||
// We still want to refund the deposit only if the para is not being offboarded.
|
||||
if req_data.confirmed {
|
||||
if !outgoing.contains(&req_id.recipient) {
|
||||
T::Currency::unreserve(
|
||||
&req_id.recipient.into_account(),
|
||||
config.hrmp_recipient_deposit.unique_saturated_into(),
|
||||
);
|
||||
}
|
||||
Self::decrease_accepted_channel_request_count(req_id.recipient);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove all storage entries associated with the given para.
|
||||
fn clean_hrmp_after_outgoing(outgoing_para: &ParaId) {
|
||||
<Self as Store>::HrmpOpenChannelRequestCount::remove(outgoing_para);
|
||||
@@ -561,7 +645,7 @@ impl<T: Config> Pallet<T> {
|
||||
|
||||
idx -= 1;
|
||||
let channel_id = open_req_channels[idx].clone();
|
||||
let mut request = <Self as Store>::HrmpOpenChannelRequests::get(&channel_id).expect(
|
||||
let request = <Self as Store>::HrmpOpenChannelRequests::get(&channel_id).expect(
|
||||
"can't be `None` due to the invariant that the list contains the same items as the set; qed",
|
||||
);
|
||||
|
||||
@@ -595,52 +679,11 @@ impl<T: Config> Pallet<T> {
|
||||
});
|
||||
}
|
||||
|
||||
let new_open_channel_req_cnt =
|
||||
<Self as Store>::HrmpOpenChannelRequestCount::get(&channel_id.sender)
|
||||
.saturating_sub(1);
|
||||
if new_open_channel_req_cnt != 0 {
|
||||
<Self as Store>::HrmpOpenChannelRequestCount::insert(
|
||||
&channel_id.sender,
|
||||
new_open_channel_req_cnt,
|
||||
);
|
||||
} else {
|
||||
<Self as Store>::HrmpOpenChannelRequestCount::remove(&channel_id.sender);
|
||||
}
|
||||
|
||||
let new_accepted_channel_req_cnt =
|
||||
<Self as Store>::HrmpAcceptedChannelRequestCount::get(&channel_id.recipient)
|
||||
.saturating_sub(1);
|
||||
if new_accepted_channel_req_cnt != 0 {
|
||||
<Self as Store>::HrmpAcceptedChannelRequestCount::insert(
|
||||
&channel_id.recipient,
|
||||
new_accepted_channel_req_cnt,
|
||||
);
|
||||
} else {
|
||||
<Self as Store>::HrmpAcceptedChannelRequestCount::remove(&channel_id.recipient);
|
||||
}
|
||||
Self::decrease_open_channel_request_count(channel_id.sender);
|
||||
Self::decrease_accepted_channel_request_count(channel_id.recipient);
|
||||
|
||||
let _ = open_req_channels.swap_remove(idx);
|
||||
<Self as Store>::HrmpOpenChannelRequests::remove(&channel_id);
|
||||
} else {
|
||||
request.age += 1;
|
||||
if request.age == config.hrmp_open_request_ttl {
|
||||
// got stale
|
||||
<Self as Store>::HrmpOpenChannelRequestCount::mutate(&channel_id.sender, |v| {
|
||||
*v -= 1;
|
||||
});
|
||||
|
||||
let _ = open_req_channels.swap_remove(idx);
|
||||
if let Some(HrmpOpenChannelRequest { sender_deposit, .. }) =
|
||||
<Self as Store>::HrmpOpenChannelRequests::take(&channel_id)
|
||||
{
|
||||
T::Currency::unreserve(
|
||||
&channel_id.sender.into_account(),
|
||||
sender_deposit.unique_saturated_into(),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
<Self as Store>::HrmpOpenChannelRequests::insert(&channel_id, request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -996,7 +1039,7 @@ impl<T: Config> Pallet<T> {
|
||||
&channel_id,
|
||||
HrmpOpenChannelRequest {
|
||||
confirmed: false,
|
||||
age: 0,
|
||||
_age: 0,
|
||||
sender_deposit: config.hrmp_sender_deposit,
|
||||
max_capacity: proposed_max_capacity,
|
||||
max_message_size: proposed_max_message_size,
|
||||
@@ -1081,12 +1124,39 @@ impl<T: Config> Pallet<T> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cancel_open_request(origin: ParaId, channel_id: HrmpChannelId) -> DispatchResult {
|
||||
// check if the origin is allowed to close the channel.
|
||||
ensure!(channel_id.is_participant(origin), Error::<T>::CancelHrmpOpenChannelUnauthorized);
|
||||
|
||||
let open_channel_req = <Self as Store>::HrmpOpenChannelRequests::get(&channel_id)
|
||||
.ok_or(Error::<T>::OpenHrmpChannelDoesntExist)?;
|
||||
ensure!(!open_channel_req.confirmed, Error::<T>::OpenHrmpChannelAlreadyConfirmed);
|
||||
|
||||
// Remove the request by the channel id and sync the accompanying list with the set.
|
||||
<Self as Store>::HrmpOpenChannelRequests::remove(&channel_id);
|
||||
<Self as Store>::HrmpOpenChannelRequestsList::mutate(|open_req_channels| {
|
||||
if let Some(pos) = open_req_channels.iter().position(|x| x == &channel_id) {
|
||||
open_req_channels.swap_remove(pos);
|
||||
}
|
||||
});
|
||||
|
||||
Self::decrease_open_channel_request_count(channel_id.sender);
|
||||
// Don't decrease `HrmpAcceptedChannelRequestCount` because we don't consider confirmed
|
||||
// requests here.
|
||||
|
||||
// Unreserve the sender's deposit. The recipient could not have left their deposit because
|
||||
// we ensured that the request is not confirmed.
|
||||
T::Currency::unreserve(
|
||||
&channel_id.sender.into_account(),
|
||||
open_channel_req.sender_deposit.unique_saturated_into(),
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn close_channel(origin: ParaId, channel_id: HrmpChannelId) -> Result<(), Error<T>> {
|
||||
// check if the origin is allowed to close the channel.
|
||||
ensure!(
|
||||
origin == channel_id.sender || origin == channel_id.recipient,
|
||||
Error::<T>::CloseHrmpChannelUnauthorized,
|
||||
);
|
||||
ensure!(channel_id.is_participant(origin), Error::<T>::CloseHrmpChannelUnauthorized);
|
||||
|
||||
// check if the channel requested to close does exist.
|
||||
ensure!(
|
||||
@@ -1167,6 +1237,30 @@ impl<T: Config> Pallet<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> Pallet<T> {
|
||||
/// Decreases the open channel request count for the given sender. If the value reaches zero
|
||||
/// it is removed completely.
|
||||
fn decrease_open_channel_request_count(sender: ParaId) {
|
||||
<Self as Store>::HrmpOpenChannelRequestCount::mutate_exists(&sender, |opt_rc| {
|
||||
*opt_rc = opt_rc.and_then(|rc| match rc.saturating_sub(1) {
|
||||
0 => None,
|
||||
n => Some(n),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// Decreases the accepted channel request count for the given sender. If the value reaches
|
||||
/// zero it is removed completely.
|
||||
fn decrease_accepted_channel_request_count(recipient: ParaId) {
|
||||
<Self as Store>::HrmpAcceptedChannelRequestCount::mutate_exists(&recipient, |opt_rc| {
|
||||
*opt_rc = opt_rc.and_then(|rc| match rc.saturating_sub(1) {
|
||||
0 => None,
|
||||
n => Some(n),
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -1231,7 +1325,6 @@ mod tests {
|
||||
hrmp_channel_max_total_size: u32,
|
||||
hrmp_sender_deposit: Balance,
|
||||
hrmp_recipient_deposit: Balance,
|
||||
hrmp_open_request_ttl: u32,
|
||||
}
|
||||
|
||||
impl Default for GenesisConfigBuilder {
|
||||
@@ -1247,7 +1340,6 @@ mod tests {
|
||||
hrmp_channel_max_total_size: 16,
|
||||
hrmp_sender_deposit: 100,
|
||||
hrmp_recipient_deposit: 100,
|
||||
hrmp_open_request_ttl: 3,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1267,7 +1359,6 @@ mod tests {
|
||||
config.hrmp_channel_max_total_size = self.hrmp_channel_max_total_size;
|
||||
config.hrmp_sender_deposit = self.hrmp_sender_deposit;
|
||||
config.hrmp_recipient_deposit = self.hrmp_recipient_deposit;
|
||||
config.hrmp_open_request_ttl = self.hrmp_open_request_ttl;
|
||||
genesis
|
||||
}
|
||||
}
|
||||
@@ -1818,35 +1909,6 @@ mod tests {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn refund_deposit_on_request_expiry() {
|
||||
let para_a = 32.into();
|
||||
let para_b = 64.into();
|
||||
|
||||
let mut genesis = GenesisConfigBuilder::default();
|
||||
genesis.hrmp_sender_deposit = 20;
|
||||
genesis.hrmp_recipient_deposit = 15;
|
||||
genesis.hrmp_open_request_ttl = 2;
|
||||
new_test_ext(genesis.build()).execute_with(|| {
|
||||
// Register two parachains funded with different amounts of funds, send an open channel
|
||||
// request but do not accept it.
|
||||
register_parachain_with_balance(para_a, 100);
|
||||
register_parachain_with_balance(para_b, 110);
|
||||
run_to_block(5, Some(vec![4, 5]));
|
||||
Hrmp::init_open_channel(para_a, para_b, 2, 8).unwrap();
|
||||
assert_eq!(<Test as Config>::Currency::free_balance(¶_a.into_account()), 80);
|
||||
assert_eq!(<Test as Config>::Currency::free_balance(¶_b.into_account()), 110);
|
||||
|
||||
// Request age is 1 out of 2
|
||||
run_to_block(10, Some(vec![10]));
|
||||
assert_eq!(<Test as Config>::Currency::free_balance(¶_a.into_account()), 80);
|
||||
|
||||
// Request age is 2 out of 2. The request should expire.
|
||||
run_to_block(20, Some(vec![20]));
|
||||
assert_eq!(<Test as Config>::Currency::free_balance(¶_a.into_account()), 100);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn refund_deposit_on_offboarding() {
|
||||
let para_a = 32.into();
|
||||
@@ -1880,4 +1942,68 @@ mod tests {
|
||||
assert_eq!(<Test as Config>::Currency::free_balance(¶_b.into_account()), 110);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_dangling_open_requests() {
|
||||
let para_a = 32.into();
|
||||
let para_b = 64.into();
|
||||
|
||||
let mut genesis = GenesisConfigBuilder::default();
|
||||
genesis.hrmp_sender_deposit = 20;
|
||||
genesis.hrmp_recipient_deposit = 15;
|
||||
new_test_ext(genesis.build()).execute_with(|| {
|
||||
// Register two parachains and open a channel between them.
|
||||
register_parachain_with_balance(para_a, 100);
|
||||
register_parachain_with_balance(para_b, 110);
|
||||
run_to_block(5, Some(vec![4, 5]));
|
||||
|
||||
// Start opening a channel a->b
|
||||
Hrmp::init_open_channel(para_a, para_b, 2, 8).unwrap();
|
||||
assert_eq!(<Test as Config>::Currency::free_balance(¶_a.into_account()), 80);
|
||||
|
||||
// Then deregister one parachain, but don't wait two sessions until it takes effect.
|
||||
// Instead, para_b will confirm the request, which will take place the same time
|
||||
// the offboarding should happen.
|
||||
deregister_parachain(para_a);
|
||||
run_to_block(9, Some(vec![9]));
|
||||
Hrmp::accept_open_channel(para_b, para_a).unwrap();
|
||||
assert_eq!(<Test as Config>::Currency::free_balance(¶_b.into_account()), 95);
|
||||
assert!(!channel_exists(para_a, para_b));
|
||||
run_to_block(10, Some(vec![10]));
|
||||
|
||||
// The outcome we expect is para_b should receive the refund.
|
||||
assert_eq!(<Test as Config>::Currency::free_balance(¶_b.into_account()), 110);
|
||||
assert!(!channel_exists(para_a, para_b));
|
||||
assert_storage_consistency_exhaustive();
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cancel_pending_open_channel_request() {
|
||||
let para_a = 32.into();
|
||||
let para_b = 64.into();
|
||||
|
||||
let mut genesis = GenesisConfigBuilder::default();
|
||||
genesis.hrmp_sender_deposit = 20;
|
||||
genesis.hrmp_recipient_deposit = 15;
|
||||
new_test_ext(genesis.build()).execute_with(|| {
|
||||
// Register two parachains and open a channel between them.
|
||||
register_parachain_with_balance(para_a, 100);
|
||||
register_parachain_with_balance(para_b, 110);
|
||||
run_to_block(5, Some(vec![4, 5]));
|
||||
|
||||
// Start opening a channel a->b
|
||||
Hrmp::init_open_channel(para_a, para_b, 2, 8).unwrap();
|
||||
assert_eq!(<Test as Config>::Currency::free_balance(¶_a.into_account()), 80);
|
||||
|
||||
// Cancel opening the channel
|
||||
Hrmp::cancel_open_request(para_a, HrmpChannelId { sender: para_a, recipient: para_b })
|
||||
.unwrap();
|
||||
assert_eq!(<Test as Config>::Currency::free_balance(¶_a.into_account()), 100);
|
||||
|
||||
run_to_block(10, Some(vec![10]));
|
||||
assert!(!channel_exists(para_a, para_b));
|
||||
assert_storage_consistency_exhaustive();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,3 +292,6 @@ WND/S
|
||||
Wococo
|
||||
XCM/S
|
||||
XCMP/M
|
||||
decrement
|
||||
DM
|
||||
ParaId
|
||||
Reference in New Issue
Block a user