Unverified Commit 4c8dda20 authored by Sergey Pepyakin's avatar Sergey Pepyakin Committed by GitHub
Browse files

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: default avatarAmar 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: default avatarAmar Singh <asinghchrony@protonmail.com>
parent ea8b360f
Pipeline #156360 passed with stages
in 45 minutes and 8 seconds
...@@ -174,7 +174,7 @@ fn default_parachains_host_configuration( ...@@ -174,7 +174,7 @@ fn default_parachains_host_configuration(
ump_service_total_weight: 4 * 1_000_000_000, ump_service_total_weight: 4 * 1_000_000_000,
max_upward_message_size: 1024 * 1024, max_upward_message_size: 1024 * 1024,
max_upward_message_num_per_candidate: 5, max_upward_message_num_per_candidate: 5,
hrmp_open_request_ttl: 5, _hrmp_open_request_ttl: 5,
hrmp_sender_deposit: 0, hrmp_sender_deposit: 0,
hrmp_recipient_deposit: 0, hrmp_recipient_deposit: 0,
hrmp_channel_max_capacity: 8, hrmp_channel_max_capacity: 8,
......
...@@ -328,7 +328,8 @@ impl<T: Encode + Decode + Default> AccountIdConversion<T> for Id { ...@@ -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 /// 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 /// 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 /// 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)] #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Encode, Decode, RuntimeDebug)]
#[cfg_attr(feature = "std", derive(Hash))] #[cfg_attr(feature = "std", derive(Hash))]
pub struct HrmpChannelId { pub struct HrmpChannelId {
...@@ -338,6 +339,13 @@ pub struct HrmpChannelId { ...@@ -338,6 +339,13 @@ pub struct HrmpChannelId {
pub recipient: Id, 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. /// A message from a parachain to its Relay Chain.
pub type UpwardMessage = Vec<u8>; pub type UpwardMessage = Vec<u8>;
......
...@@ -11,8 +11,6 @@ HRMP related structs: ...@@ -11,8 +11,6 @@ HRMP related structs:
struct HrmpOpenChannelRequest { struct HrmpOpenChannelRequest {
/// Indicates if this request was confirmed by the recipient. /// Indicates if this request was confirmed by the recipient.
confirmed: bool, 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. /// The amount that the sender supplied at the time of creation of this request.
sender_deposit: Balance, sender_deposit: Balance,
/// The maximum message size that could be put into the channel. /// The maximum message size that could be put into the channel.
...@@ -158,8 +156,8 @@ Candidate Enactment: ...@@ -158,8 +156,8 @@ Candidate Enactment:
1. Decrement `C.msg_count` 1. Decrement `C.msg_count`
1. Decrement `C.total_size` by `M`'s payload size. 1. Decrement `C.total_size` by `M`'s payload size.
1. Set `HrmpWatermarks` for `P` to be equal to `new_hrmp_watermark` 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 > NOTE: That collecting digests can be inefficient and the time it takes grows very fast. Thanks to the aggressive
> parametrization this shouldn't be a big of a deal. > 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 > If that becomes a problem consider introducing an extra dictionary which says at what block the given sender
> sent a message to the recipient. > sent a message to the recipient.
...@@ -212,6 +210,13 @@ the parachain executed the message. ...@@ -212,6 +210,13 @@ the parachain executed the message.
- The DM is sent using `queue_downward_message`. - The DM is sent using `queue_downward_message`.
- The DM is represented by the `HrmpChannelAccepted` XCM message. - The DM is represented by the `HrmpChannelAccepted` XCM message.
- `recipient` is set to `origin`. - `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)`: * `hrmp_close_channel(ch)`:
1. Check that `origin` is either `ch.sender` or `ch.recipient` 1. Check that `origin` is either `ch.sender` or `ch.recipient`
1. Check that `HrmpChannels` for `ch` exists. 1. Check that `HrmpChannels` for `ch` exists.
...@@ -233,15 +238,12 @@ the parachain executed the message. ...@@ -233,15 +238,12 @@ the parachain executed the message.
1. Remove all outbound channels of `P`, i.e. `(P, _)`, 1. Remove all outbound channels of `P`, i.e. `(P, _)`,
1. Remove `HrmpOpenChannelRequestCount` for `P` 1. Remove `HrmpOpenChannelRequestCount` for `P`
1. Remove `HrmpAcceptedChannelRequestCount` 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. For each channel designator `D` in `HrmpOpenChannelRequestsList` we query the request `R` from `HrmpOpenChannelRequests`:
1. if `R.confirmed = false`: 1. if `R.confirmed = true`,
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 both `D.sender` and `D.recipient` are not offboarded. 1. if both `D.sender` and `D.recipient` are not offboarded.
1. create a new channel `C` between `(D.sender, D.recipient)`. 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` 1. Initialize the `C.sender_deposit` with `R.sender_deposit` and `C.recipient_deposit`
......
...@@ -42,7 +42,7 @@ struct HostConfiguration { ...@@ -42,7 +42,7 @@ struct HostConfiguration {
pub dispute_period: SessionIndex, pub dispute_period: SessionIndex,
/// How long after dispute conclusion to accept statements. /// How long after dispute conclusion to accept statements.
pub dispute_post_conclusion_acceptance_period: BlockNumber, 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, pub dispute_max_spam_slots: u32,
/// How long it takes for a dispute to conclude by time-out, if no supermajority is reached. /// 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, pub dispute_conclusion_by_time_out_period: BlockNumber,
...@@ -85,8 +85,6 @@ struct HostConfiguration { ...@@ -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 /// decide to do with its PoV so this value in practice will be picked as a fraction of the PoV
/// size. /// size.
pub max_downward_message_size: u32, 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. /// The deposit that the sender should provide for opening an HRMP channel.
pub hrmp_sender_deposit: u32, pub hrmp_sender_deposit: u32,
/// The deposit that the recipient should provide for accepting opening an HRMP channel. /// The deposit that the recipient should provide for accepting opening an HRMP channel.
...@@ -119,7 +117,7 @@ struct HostConfiguration { ...@@ -119,7 +117,7 @@ struct HostConfiguration {
Inherent data passed to a runtime entry-point for the advancement of parachain consensus. Inherent data passed to a runtime entry-point for the advancement of parachain consensus.
This contains 3 pieces of data: 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) 2. [`BackedCandidates`](backing.md#backed-candidate)
3. [`MultiDisputeStatementSet`](disputes.md#multidisputestatementset) 3. [`MultiDisputeStatementSet`](disputes.md#multidisputestatementset)
......
...@@ -91,8 +91,10 @@ pub struct HostConfiguration<BlockNumber> { ...@@ -91,8 +91,10 @@ pub struct HostConfiguration<BlockNumber> {
pub hrmp_max_parachain_outbound_channels: u32, pub hrmp_max_parachain_outbound_channels: u32,
/// The maximum number of outbound HRMP channels a parathread is allowed to open. /// The maximum number of outbound HRMP channels a parathread is allowed to open.
pub hrmp_max_parathread_outbound_channels: u32, pub hrmp_max_parathread_outbound_channels: u32,
/// Number of sessions after which an HRMP open channel request expires. /// NOTE: this field is deprecated. Channel open requests became non-expiring. Changing this value
pub hrmp_open_request_ttl: u32, /// 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. /// The deposit that the sender should provide for opening an HRMP channel.
pub hrmp_sender_deposit: Balance, pub hrmp_sender_deposit: Balance,
/// The deposit that the recipient should provide for accepting opening an HRMP channel. /// 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 ...@@ -202,7 +204,7 @@ impl<BlockNumber: Default + From<u32>> Default for HostConfiguration<BlockNumber
ump_service_total_weight: Default::default(), ump_service_total_weight: Default::default(),
max_upward_message_size: Default::default(), max_upward_message_size: Default::default(),
max_upward_message_num_per_candidate: 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_sender_deposit: Default::default(),
hrmp_recipient_deposit: Default::default(), hrmp_recipient_deposit: Default::default(),
hrmp_channel_max_capacity: Default::default(), hrmp_channel_max_capacity: Default::default(),
...@@ -641,12 +643,10 @@ pub mod pallet { ...@@ -641,12 +643,10 @@ pub mod pallet {
/// Sets the number of sessions after which an HRMP open channel request expires. /// Sets the number of sessions after which an HRMP open channel request expires.
#[pallet::weight((1_000, DispatchClass::Operational))] #[pallet::weight((1_000, DispatchClass::Operational))]
pub fn set_hrmp_open_request_ttl(origin: OriginFor<T>, new: u32) -> DispatchResult { // Deprecated, but is not marked as such, because that would trigger warnings coming from
ensure_root(origin)?; // the macro.
Self::update_config_member(|config| { pub fn set_hrmp_open_request_ttl(_origin: OriginFor<T>, _new: u32) -> DispatchResult {
sp_std::mem::replace(&mut config.hrmp_open_request_ttl, new) != new Err("this doesn't have any effect".into())
});
Ok(())
} }
/// Sets the amount of funds that the sender should provide for opening an HRMP channel. /// Sets the amount of funds that the sender should provide for opening an HRMP channel.
...@@ -888,7 +888,7 @@ mod tests { ...@@ -888,7 +888,7 @@ mod tests {
ump_service_total_weight: 20000, ump_service_total_weight: 20000,
max_upward_message_size: 448, max_upward_message_size: 448,
max_upward_message_num_per_candidate: 5, max_upward_message_num_per_candidate: 5,
hrmp_open_request_ttl: 1312, _hrmp_open_request_ttl: 0,
hrmp_sender_deposit: 22, hrmp_sender_deposit: 22,
hrmp_recipient_deposit: 4905, hrmp_recipient_deposit: 4905,
hrmp_channel_max_capacity: 3921, hrmp_channel_max_capacity: 3921,
...@@ -1013,11 +1013,6 @@ mod tests { ...@@ -1013,11 +1013,6 @@ mod tests {
new_config.max_upward_message_num_per_candidate, new_config.max_upward_message_num_per_candidate,
) )
.unwrap(); .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) Configuration::set_hrmp_sender_deposit(Origin::root(), new_config.hrmp_sender_deposit)
.unwrap(); .unwrap();
Configuration::set_hrmp_recipient_deposit( Configuration::set_hrmp_recipient_deposit(
......
...@@ -39,8 +39,9 @@ pub use pallet::*; ...@@ -39,8 +39,9 @@ pub use pallet::*;
pub struct HrmpOpenChannelRequest { pub struct HrmpOpenChannelRequest {
/// Indicates if this request was confirmed by the recipient. /// Indicates if this request was confirmed by the recipient.
pub confirmed: bool, pub confirmed: bool,
/// How many session boundaries ago this request was seen. /// NOTE: this field is deprecated. Channel open requests became non-expiring and this value
pub age: SessionIndex, /// became unused.
pub _age: SessionIndex,
/// The amount that the sender supplied at the time of creation of this request. /// The amount that the sender supplied at the time of creation of this request.
pub sender_deposit: Balance, pub sender_deposit: Balance,
/// The maximum message size that could be put into the channel. /// The maximum message size that could be put into the channel.
...@@ -200,6 +201,9 @@ pub mod pallet { ...@@ -200,6 +201,9 @@ pub mod pallet {
/// Open HRMP channel requested. /// Open HRMP channel requested.
/// `[sender, recipient, proposed_max_capacity, proposed_max_message_size]` /// `[sender, recipient, proposed_max_capacity, proposed_max_message_size]`
OpenChannelRequested(ParaId, ParaId, u32, u32), 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]` /// Open HRMP channel accepted. `[sender, recipient]`
OpenChannelAccepted(ParaId, ParaId), OpenChannelAccepted(ParaId, ParaId),
/// HRMP channel closed. `[by_parachain, channel_id]` /// HRMP channel closed. `[by_parachain, channel_id]`
...@@ -238,6 +242,12 @@ pub mod pallet { ...@@ -238,6 +242,12 @@ pub mod pallet {
CloseHrmpChannelDoesntExist, CloseHrmpChannelDoesntExist,
/// The channel close request is already requested. /// The channel close request is already requested.
CloseHrmpChannelAlreadyUnderway, 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. /// The set of pending HRMP open channel requests.
...@@ -464,6 +474,22 @@ pub mod pallet { ...@@ -464,6 +474,22 @@ pub mod pallet {
Self::process_hrmp_close_channel_requests(); Self::process_hrmp_close_channel_requests();
Ok(()) 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> { ...@@ -507,19 +533,77 @@ impl<T: Config> Pallet<T> {
notification: &initializer::SessionChangeNotification<T::BlockNumber>, notification: &initializer::SessionChangeNotification<T::BlockNumber>,
outgoing_paras: &[ParaId], outgoing_paras: &[ParaId],
) { ) {
Self::perform_outgoing_para_cleanup(outgoing_paras); Self::perform_outgoing_para_cleanup(&notification.prev_config, outgoing_paras);
Self::process_hrmp_open_channel_requests(&notification.prev_config); Self::process_hrmp_open_channel_requests(&notification.prev_config);
Self::process_hrmp_close_channel_requests(); Self::process_hrmp_close_channel_requests();
} }
/// Iterate over all paras that were noted for offboarding and remove all the data /// Iterate over all paras that were noted for offboarding and remove all the data
/// associated with them. /// 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 { for outgoing_para in outgoing {
Self::clean_hrmp_after_outgoing(outgoing_para); 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. /// Remove all storage entries associated with the given para.
fn clean_hrmp_after_outgoing(outgoing_para: &ParaId) { fn clean_hrmp_after_outgoing(outgoing_para: &ParaId) {
<Self as Store>::HrmpOpenChannelRequestCount::remove(outgoing_para); <Self as Store>::HrmpOpenChannelRequestCount::remove(outgoing_para);
...@@ -561,7 +645,7 @@ impl<T: Config> Pallet<T> { ...@@ -561,7 +645,7 @@ impl<T: Config> Pallet<T> {
idx -= 1; idx -= 1;
let channel_id = open_req_channels[idx].clone(); 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", "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> { ...@@ -595,52 +679,11 @@ impl<T: Config> Pallet<T> {
}); });
} }
let new_open_channel_req_cnt = Self::decrease_open_channel_request_count(channel_id.sender);
<Self as Store>::HrmpOpenChannelRequestCount::get(&channel_id.sender) Self::decrease_accepted_channel_request_count(channel_id.recipient);
.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);
}
let _ = open_req_channels.swap_remove(idx); let _ = open_req_channels.swap_remove(idx);
<Self as Store>::HrmpOpenChannelRequests::remove(&channel_id); <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> { ...@@ -996,7 +1039,7 @@ impl<T: Config> Pallet<T> {
&channel_id, &channel_id,
HrmpOpenChannelRequest { HrmpOpenChannelRequest {
confirmed: false, confirmed: false,
age: 0, _age: 0,
sender_deposit: config.hrmp_sender_deposit, sender_deposit: config.hrmp_sender_deposit,
max_capacity: proposed_max_capacity, max_capacity: proposed_max_capacity,
max_message_size: proposed_max_message_size, max_message_size: proposed_max_message_size,
...@@ -1081,13 +1124,40 @@ impl<T: Config> Pallet<T> { ...@@ -1081,13 +1124,40 @@ impl<T: Config> Pallet<T> {
Ok(()) Ok(())
} }
fn close_channel(origin: ParaId, channel_id: HrmpChannelId) -> Result<(), Error<T>> { fn cancel_open_request(origin: ParaId, channel_id: HrmpChannelId) -> DispatchResult {
// check if the origin is allowed to close the channel. // check if the origin is allowed to close the channel.
ensure!( ensure!(channel_id.is_participant(origin), Error::<T>::CancelHrmpOpenChannelUnauthorized);
origin == channel_id.sender || origin == channel_id.recipient,
Error::<T>::CloseHrmpChannelUnauthorized, 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!(channel_id.is_participant(origin), Error::<T>::CloseHrmpChannelUnauthorized);
// check if the channel requested to close does exist. // check if the channel requested to close does exist.
ensure!( ensure!(
<Self as Store>::HrmpChannels::get(&channel_id).is_some(), <Self as Store>::HrmpChannels::get(&channel_id).is_some(),
...@@ -1167,6 +1237,30 @@ impl<T: Config> Pallet<T> { ...@@ -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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
...@@ -1231,7 +1325,6 @@ mod tests { ...@@ -1231,7 +1325,6 @@ mod tests {
hrmp_channel_max_total_size: u32, hrmp_channel_max_total_size: u32,
hrmp_sender_deposit: Balance, hrmp_sender_deposit: Balance,
hrmp_recipient_deposit: Balance, hrmp_recipient_deposit: Balance,
hrmp_open_request_ttl: u32,
} }
impl Default for GenesisConfigBuilder { impl Default for GenesisConfigBuilder {
...@@ -1247,7 +1340,6 @@ mod tests { ...@@ -1247,7 +1340,6 @@ mod tests {
hrmp_channel_max_total_size: 16, hrmp_channel_max_total_size: 16,
hrmp_sender_deposit: 100, hrmp_sender_deposit: 100,
hrmp_recipient_deposit: 100, hrmp_recipient_deposit: 100,
hrmp_open_request_ttl: 3,
} }
}