mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-17 17:11:02 +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,
|
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 {
|
|||||||
/// 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 {
|
|||||||
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:
|
|||||||
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:
|
|||||||
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.
|
|||||||
- 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.
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
/// 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 {
|
|||||||
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> {
|
|||||||
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
|
|||||||
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 {
|
|||||||
|
|
||||||
/// 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 {
|
|||||||
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 {
|
|||||||
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::*;
|
|||||||
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 {
|
|||||||
/// 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 {
|
|||||||
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 {
|
|||||||
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> {
|
|||||||
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(¬ification.prev_config, outgoing_paras);
|
||||||
Self::process_hrmp_open_channel_requests(¬ification.prev_config);
|
Self::process_hrmp_open_channel_requests(¬ification.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> {
|
|||||||
|
|
||||||
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> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
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> {
|
|||||||
&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,12 +1124,39 @@ impl<T: Config> Pallet<T> {
|
|||||||
Ok(())
|
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>> {
|
fn close_channel(origin: ParaId, channel_id: HrmpChannelId) -> Result<(), Error<T>> {
|
||||||
// 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>::CloseHrmpChannelUnauthorized);
|
||||||
origin == channel_id.sender || origin == channel_id.recipient,
|
|
||||||
Error::<T>::CloseHrmpChannelUnauthorized,
|
|
||||||
);
|
|
||||||
|
|
||||||
// check if the channel requested to close does exist.
|
// check if the channel requested to close does exist.
|
||||||
ensure!(
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -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 {
|
|||||||
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,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1267,7 +1359,6 @@ mod tests {
|
|||||||
config.hrmp_channel_max_total_size = self.hrmp_channel_max_total_size;
|
config.hrmp_channel_max_total_size = self.hrmp_channel_max_total_size;
|
||||||
config.hrmp_sender_deposit = self.hrmp_sender_deposit;
|
config.hrmp_sender_deposit = self.hrmp_sender_deposit;
|
||||||
config.hrmp_recipient_deposit = self.hrmp_recipient_deposit;
|
config.hrmp_recipient_deposit = self.hrmp_recipient_deposit;
|
||||||
config.hrmp_open_request_ttl = self.hrmp_open_request_ttl;
|
|
||||||
genesis
|
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]
|
#[test]
|
||||||
fn refund_deposit_on_offboarding() {
|
fn refund_deposit_on_offboarding() {
|
||||||
let para_a = 32.into();
|
let para_a = 32.into();
|
||||||
@@ -1880,4 +1942,68 @@ mod tests {
|
|||||||
assert_eq!(<Test as Config>::Currency::free_balance(¶_b.into_account()), 110);
|
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
|
Wococo
|
||||||
XCM/S
|
XCM/S
|
||||||
XCMP/M
|
XCMP/M
|
||||||
|
decrement
|
||||||
|
DM
|
||||||
|
ParaId
|
||||||
Reference in New Issue
Block a user