diff --git a/roadmap/implementers-guide/src/messaging.md b/roadmap/implementers-guide/src/messaging.md index 675563758990..62fbe3cfea88 100644 --- a/roadmap/implementers-guide/src/messaging.md +++ b/roadmap/implementers-guide/src/messaging.md @@ -36,9 +36,10 @@ The weight that processing of the dispatchables can consume is limited by a prec that some dispatchables will be left for later blocks. To make the dispatching more fair, the queues are processed turn-by-turn in a round robin fashion. -Other kinds of upward messages can be introduced in the future as well. Potential candidates are channel management for -horizontal message passing (XCMP and HRMP, both are to be described below), new validation code signalling, or other -requests to the relay chain. +Upward messages are also used by a parachain to request opening and closing HRMP channels (HRMP will be described below). + +Other kinds of upward messages can be introduced in the future as well. Potential candidates are +new validation code signalling, or other requests to the relay chain. ## Horizontal Message Passing diff --git a/roadmap/implementers-guide/src/runtime/router.md b/roadmap/implementers-guide/src/runtime/router.md index 55013fa03b63..6649d32a9ea7 100644 --- a/roadmap/implementers-guide/src/runtime/router.md +++ b/roadmap/implementers-guide/src/runtime/router.md @@ -40,10 +40,6 @@ struct HrmpChannelId { /// A description of a request to open an HRMP channel. struct HrmpOpenChannelRequest { - /// The sender and the initiator of this request. - sender: ParaId, - /// The recipient of the opened request. - recipient: ParaId, /// Indicates if this request was confirmed by the recipient. confirmed: bool, /// How many session boundaries ago this request was seen. @@ -56,19 +52,6 @@ struct HrmpOpenChannelRequest { limit_used_bytes: u32, } -/// A description of a request to close an opened HRMP channel. -struct HrmpCloseChannelRequest { - /// The para which initiated closing an existing channel. - /// - /// Invariant: it should be equal to one of the following - /// - `Some(sender)` - /// - `Some(recipient)` - /// - `None` in case the request initiated by the runtime (including governance, migration, etc) - initiator: Option, - /// The identifier of an HRMP channel to be closed. - id: HrmpChannelId, -} - /// A metadata of an HRMP channel. struct HrmpChannel { /// The amount that the sender supplied as a deposit when opening this channel. @@ -98,14 +81,30 @@ struct HrmpChannel { HRMP related storage layout ```rust,ignore -/// Pending HRMP open channel requests. -HrmpOpenChannelRequests: Vec; +/// The set of pending HRMP open channel requests. +/// +/// The set is accompanied by a list for iteration. +/// +/// Invariant: +/// - There are no channels that exists in list but not in the set and vice versa. +HrmpOpenChannelRequests: map HrmpChannelId => Option; +HrmpOpenChannelRequestsList: Vec; + /// This mapping tracks how many open channel requests are inititated by a given sender para. -/// Invariant: `HrmpOpenChannelRequests` should contain the same number of items that has `_.sender == X` +/// Invariant: `HrmpOpenChannelRequestsList` should contain the same number of items that has `(X, _)` /// as the number of `HrmpOpenChannelRequestCount` for `X`. HrmpOpenChannelRequestCount: map ParaId => u32; -/// Pending HRMP close channel requests. -HrmpCloseChannelRequests: Vec; + +/// A set of pending HRMP close channel requests that are going to be closed during the session change. +/// Used for checking if a given channel is registered for closure. +/// +/// The set is accompanied by a list for iteration. +/// +/// Invariant: +/// - There are no channels that exists in list but not in the set and vice versa. +HrmpCloseChannelRequests: map HrmpChannelId => Option<()>; +HrmpCloseChannelRequestsList: Vec; + /// The HRMP watermark associated with each para. HrmpWatermarks: map ParaId => Option; /// HRMP channel data associated with each para. @@ -129,44 +128,32 @@ HrmpChannelDigests: map ParaId => Vec<(BlockNumber, Vec)>; No initialization routine runs for this module. -## Entry points - -The following routines are intended to be invoked by paras' upward messages. - -* `hrmp_init_open_channel(recipient)`: - 1. Check that the origin of this message is a para. We say that the origin of this message is the `sender`. - 1. Check that the `sender` is not `recipient`. - 1. Check that the `recipient` para exists. - 1. Check that there is no existing open channel request from sender to recipient. - 1. Check that the sum of the number of already opened HRMP channels by the `sender` (the size of the set found `HrmpEgressChannelsIndex` for `sender`) and the number of open requests by the `sender` (the value from `HrmpOpenChannelRequestCount` for `sender`) doesn't exceed the limit of channels (`config.hrmp_max_parachain_outbound_channels` or `config.hrmp_max_parathread_outbound_channels`) minus 1. - 1. Reserve the deposit for the `sender` according to `config.hrmp_sender_deposit` - 1. Add a new entry to `HrmpOpenChannelRequests` and increase `HrmpOpenChannelRequestCount` by 1 for the `sender`. - 1. Set `sender_deposit` to `config.hrmp_sender_deposit` - 1. Set `limit_used_places` to `config.hrmp_channel_max_places` - 1. Set `limit_limit_used_bytes` to `config.hrmp_channel_max_size` - -* `hrmp_accept_open_channel(i)`, `i` - is the index of open channel request: - 1. Check that the designated open channel request exists - 1. Check that the request's `recipient` corresponds to the origin of this message. - 1. Reserve the deposit for the `recipient` according to `config.hrmp_recipient_deposit` - 1. Set the request's `confirmed` flag to `true`. - -* `hrmp_close_channel(sender, recipient)`: - 1. Check that the channel between `sender` and `recipient` exists - 1. Check that the origin of the message is either `sender` or `recipient` - 1. Check that there is no existing intention to close the channel between `sender` and `recipient`. - 1. Add a new entry to `HrmpCloseChannelRequests` with initiator set to the `Some` variant with the origin of this message. - ## Routines Candidate Acceptance Function: * `check_upward_messages(P: ParaId, Vec`: 1. Checks that there are at most `config.max_upward_message_num_per_candidate` messages. - 1. Checks each upward message individually depending on its kind: + 1. Checks each upward message `M` individually depending on its kind: 1. If the message kind is `Dispatchable`: 1. Verify that `RelayDispatchQueueSize` for `P` has enough capacity for the message (NOTE that should include all processed upward messages of the `Dispatchable` kind up to this point!) + 1. If the message kind is `HrmpInitOpenChannel(recipient)`: + 1. Check that the `P` is not `recipient`. + 1. Check that `recipient` is a valid para. + 1. Check that there is no existing open channel request (`P`, `recipient`) in `HrmpOpenChannelRequests`. + 1. Check that the sum of the number of already opened HRMP channels by the `sender` (the size + of the set found `HrmpEgressChannelsIndex` for `sender`) and the number of open requests by the + `sender` (the value from `HrmpOpenChannelRequestCount` for `sender`) doesn't exceed the limit of + channels (`config.hrmp_max_parachain_outbound_channels` or `config.hrmp_max_parathread_outbound_channels`) minus 1. + 1. Check that `P`'s balance is more or equal to `config.hrmp_sender_deposit` + 1. If the message kind is `HrmpAcceptOpenChannel(sender)`: + 1. Check that there is existing request between (`sender`, `P`) in `HrmpOpenChannelRequests` + 1. Check that `P`'s balance is more or equal to `config.hrmp_recipient_deposit`. + 1. If the message kind is `HrmpCloseChannel(ch)`: + 1. Check that `P` is either `ch.sender` or `ch.recipient` + 1. Check that `HrmpChannels` for `ch` exists. + 1. Check that `ch` is not in the `HrmpCloseChannelRequests` set. * `check_processed_downward_messages(P: ParaId, processed_downward_messages)`: 1. Checks that `DownwardMessageQueues` for `P` is at least `processed_downward_messages` long. 1. Checks that `processed_downward_messages` is at least 1 if `DownwardMessageQueues` for `P` is not empty. @@ -205,6 +192,20 @@ Candidate Enactment: 1. Append the message to `RelayDispatchQueues` for `P` 1. Increment the size and the count in `RelayDispatchQueueSize` for `P`. 1. Ensure that `P` is present in `NeedsDispatch`. + 1. If the message kind is `HrmpInitOpenChannel(recipient)`: + 1. Increase `HrmpOpenChannelRequestCount` by 1 for the `P`. + 1. Append `(P, recipient)` to `HrmpOpenChannelRequestsList`. + 1. Add a new entry to `HrmpOpenChannelRequests` for `(sender, recipient)` + 1. Set `sender_deposit` to `config.hrmp_sender_deposit` + 1. Set `limit_used_places` to `config.hrmp_channel_max_places` + 1. Set `limit_limit_used_bytes` to `config.hrmp_channel_max_size` + 1. Reserve the deposit for the `P` according to `config.hrmp_sender_deposit` + 1. If the message kind is `HrmpAcceptOpenChannel(sender)`: + 1. Reserve the deposit for the `P` according to `config.hrmp_recipient_deposit` + 1. For the request in `HrmpOpenChannelRequests` identified by `(sender, P)`, set `confirmed` flag to `true`. + 1. If the message kind is `HrmpCloseChannel(ch)`: + 1. Insert a new entry `Some(())` to `HrmpCloseChannelRequests` for `ch`. + 1. Append `ch` to `HrmpCloseChannelRequestsList`. The following routine is intended to be called in the same time when `Paras::schedule_para_cleanup` is called. @@ -242,23 +243,29 @@ any of dispatchables return an error. 1. Remove `RelayDispatchQueues` of `P`. 1. Remove `P` if it exists in `NeedsDispatch`. 1. If `P` is in `NextDispatchRoundStartWith`, then reset it to `None` - - Note that we don't remove the open/close requests since they are gon die out naturally. -1. For each request `R` in `HrmpOpenChannelRequests`: + - Note that if we don't remove the open/close requests since they are going to die out naturally at the end of the session. +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 `R.sender` by 1. + 1. decrement `HrmpOpenChannelRequestCount` for `D.sender` by 1. 1. remove `R` + 1. remove `D` 2. if `R.confirmed = true`, - 1. check that `R.sender` and `R.recipient` are not offboarded. - 1. create a new channel `C` between `(R.sender, R.recipient)`. - 1. Initialize the `C.sender_deposit` with `R.sender_deposit` and `C.recipient_deposit` with the value found in the configuration `config.hrmp_recipient_deposit`. - 1. Insert `sender` into the set `HrmpIngressChannelsIndex` for the `recipient`. - 1. Insert `recipient` into the set `HrmpEgressChannelsIndex` for the `sender`. - 1. decrement `HrmpOpenChannelRequestCount` for `R.sender` by 1. + 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` + with the value found in the configuration `config.hrmp_recipient_deposit`. + 1. Insert `sender` into the set `HrmpIngressChannelsIndex` for the `recipient`. + 1. Insert `recipient` into the set `HrmpEgressChannelsIndex` for the `sender`. + 1. decrement `HrmpOpenChannelRequestCount` for `D.sender` by 1. 1. remove `R` -1. For each request `R` in `HrmpCloseChannelRequests` remove the channel identified by `R.id`, if exists. + 1. remove `D` +1. For each channel designator `D` in `HrmpCloseChannelRequestsList` + 1. remove the channel identified by `D`, if exists. + 1. remove `D` from `HrmpCloseChannelRequests`. + 1. remove `D` from `HrmpCloseChannelRequestsList` To remove a channel `C` identified with a tuple `(sender, recipient)`: diff --git a/roadmap/implementers-guide/src/types/messages.md b/roadmap/implementers-guide/src/types/messages.md index af4416670789..b3ab8ed7ce4c 100644 --- a/roadmap/implementers-guide/src/types/messages.md +++ b/roadmap/implementers-guide/src/types/messages.md @@ -37,9 +37,23 @@ enum UpwardMessage { /// The dispatchable to be executed in its raw form. dispatchable: RawDispatchable, }, - // Examples: - // HrmpOpenChannel { .. }, - // HrmpCloseChannel { .. }, + /// A message for initiation of opening a new HRMP channel between the origin para and the + /// given `recipient`. + /// + /// Let `origin` be the parachain that sent this upward message. In that case the channel + /// to be opened is (`origin` -> `recipient`). + HrmpInitOpenChannel(ParaId), + /// A message that is meant to confirm the HRMP open channel request initiated earlier by the + /// `HrmpInitOpenChannel` by the given `sender`. + /// + /// Let `origin` be the parachain that sent this upward message. In that case the channel + /// (`origin` -> `sender`) will be opened during the session change. + HrmpAcceptOpenChannel(ParaId), + /// A message for closing the specified existing channel `ch`. + /// + /// The channel to be closed is `(ch.sender -> ch.recipient)`. The parachain that sent this + /// upward message must be either `ch.sender` or `ch.recipient`. + HrmpCloseChannel(HrmpChannelId), } ```