-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Implementer's guide: downward messages and HRMP #1399
Changes from all commits
c90fdf4
d1ab1fb
69fe295
ceaa096
e952a01
778cc2b
bdb97af
921ca3b
3fff854
8072356
a622f77
0309bc5
1c75b51
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -68,13 +68,18 @@ All failed checks should lead to an unrecoverable error making the block invalid | |
| 1. Transform each [`CommittedCandidateReceipt`](../types/candidate.md#committed-candidate-receipt) into the corresponding [`CandidateReceipt`](../types/candidate.md#candidate-receipt), setting the commitments aside. | ||
| 1. check the backing of the candidate using the signatures and the bitfields, comparing against the validators assigned to the groups, fetched with the `group_validators` lookup. | ||
| 1. check that the upward messages, when combined with the existing queue size, are not exceeding `config.max_upward_queue_count` and `config.watermark_upward_queue_size` parameters. | ||
| 1. call `Router::ensure_processed_downward_messages(para, commitments.processed_downward_messages)` for each candidate to check rules of processing the downward message queue. | ||
| 1. check that in the commitments of each candidate the horizontal messages are sorted by ascending recipient ParaId and there is no two horizontal messages have the same recipient. | ||
| 1. using `Router::ensure_horizontal_messages_fit(sender, commitments.horizontal_messages)` ensure that the each candidate's para doesn't overfill any downward queue. | ||
| 1. create an entry in the `PendingAvailability` map for each backed candidate with a blank `availability_votes` bitfield. | ||
| 1. create a corresponding entry in the `PendingAvailabilityCommitments` with the commitments. | ||
| 1. Return a `Vec<CoreIndex>` of all scheduled cores of the list of passed assignments that a candidate was successfully backed for, sorted ascending by CoreIndex. | ||
| * `enact_candidate(relay_parent_number: BlockNumber, CommittedCandidateReceipt)`: | ||
| 1. If the receipt contains a code upgrade, Call `Paras::schedule_code_upgrade(para_id, code, relay_parent_number + config.validationl_upgrade_delay)`. | ||
| > TODO: Note that this is safe as long as we never enact candidates where the relay parent is across a session boundary. In that case, which we should be careful to avoid with contextual execution, the configuration might have changed and the para may de-sync from the host's understanding of it. | ||
| 1. call `Router::queue_upward_messages` for each backed candidate, using the [`UpwardMessage`s](../types/messages.md#upward-message) from the [`CandidateCommitments`](../types/candidate.md#candidate-commitments). | ||
| 1. call `Router::drain_downward_messages` with the para id of the candidate and `processed_downward_messages` taken from the commitment, | ||
| 1. call `Router::queue_horizontal_messages` with the para id of the candidate and the list of horizontal messages taken from the commitment, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. likewise: the name here doesn't match what is in the
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| 1. Call `Paras::note_new_head` using the `HeadData` from the receipt and `relay_parent_number`. | ||
| * `collect_pending`: | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,6 @@ | ||
| # Router Module | ||
|
|
||
| The Router module is responsible for storing and dispatching Upward and Downward messages from and to parachains respectively. It is intended to later handle the XCMP logic as well. | ||
|
|
||
| For each enacted block the `queue_upward_messages` entry-point is called. | ||
| The Router module is responsible for all messaging mechanisms supported between paras and the relay chain, specifically: UMP, DMP, HRMP and later XCMP. | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it would be better to add a separate page for describing those in detail and linking this page here. As for now, I added those in the glossary. |
||
|
|
||
| ## Storage | ||
|
|
||
|
|
@@ -19,6 +17,17 @@ RelayDispatchQueues: map ParaId => Vec<UpwardMessage>; | |
| RelayDispatchQueueSize: map ParaId => (u32, u32); | ||
| /// The ordered list of `ParaId`s that have a `RelayDispatchQueue` entry. | ||
| NeedsDispatch: Vec<ParaId>; | ||
| /// The mapping that tracks how many bytes / messages are sent by a certain sender - recipient pair. | ||
| /// | ||
| /// First item in the tuple is the count of messages for the (sender, recipient) pair and the second | ||
| /// item is the total length (in bytes) of the message payloads. | ||
| HorizontalMessagesResourceUsage: map (ParaId, ParaId) => (u32, u32); | ||
| /// The downward messages addressed for a certain para. These vectors are not bounded directly, but | ||
| /// rather each possible sender can put only a limited amount of messages in the downward queue. | ||
| DownwardMessageQueues: map ParaId => Vec<DownwardMessage>; | ||
| /// The number of downward messages originated from the relay chain to a certain para. This is subject | ||
| /// to the `max_relay_chain_downward_messages` limit found in `HostConfiguration`. | ||
| RelayChainDownwardMessages: map ParaId => u32; | ||
| ``` | ||
|
|
||
| ## Initialization | ||
|
|
@@ -27,6 +36,61 @@ No initialization routine runs for this module. | |
|
|
||
| ## Routines | ||
|
|
||
| There are situations when actions that took place within the relay chain could lead to a downward message | ||
| sent to a para. For example, if an entry-point to transfer some funds to a para was called. | ||
|
|
||
| For these cases, there are two routines, `has_dmq_capacity_for_relay_chain` and `send_downward_messages`, | ||
| intended for use by the relay chain. | ||
|
|
||
| `send_downward_messages` is used for enqueuing one or more downward messages for a certain recipient. Since downward | ||
| message queues can hold only so many messages per one sender (and the relay chain is not an exception), | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why should the Relay chain not be an exception? It is the Relay chain which must store these messages, so I don't see why it shouldn't be able to determine when it is acceptable to add some item into the queue. Furthermore by limiting this, then other actions which need to be infallible (such as fund transfer notifications which happen on a scheduled block) may become impossible or result in the need for additional unbounded queues also living on the relay chain. Better, surely, to keep things simple and just allow the relay chain code (which we trust anyway) to add messages into the queue as it sees fit.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Funnily, one of the reasons for this limitation was actually simplicity. Since there is, atm unbounded, set of downward message kinds it seemed to me the simplest to just slap a single limit on a downward message queue (DMQ) instead of coming up with individual mechanisms that prevent DoS for each operation that could lead to a downward message (DM) send. For example, one of such operations, is sending some funds into a parachain account which results in a DM. We of course trust the relay chain, but this particular operation can be executed by a signed extrinsic and therefore can be abused by sending dust transfers. Since HRMP piggybacks on DMQ in this proposal, regular users of the relay-chain would be able to compete with the HRMP messages from other paras. Users would have had an advantage since HRMP is limited by 1 message per candidate. Surely, there should be some sort of mitigation in place to prevent that. We could go with the ad-hoc mitigations. E.g. for transfer into para there is a simple mitigation: we can coalesce all incoming transfers per block into a single DM. Perhaps we would have to omit the senders for the sake of avoiding inflating the DM's size, but that might limit the usefulness of such message somewhat. For each future operation that might need to send a DM we would need to come up with a mitigation as well, and it might not be that simple. The alternative is just slap a limit on each DMQ, if there is no space to send a message just bail and call it a day. As you noticed, that would rule out a certain kind of operations, specifically those which cannot check the capacity beforehand. So it is not just simple, it's too simplistic to the degree that's it is also too limiting. Therefore, it seems that we have to bite the bullet and go with limitless relay-chain and ad-hoc mitigations.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, basically for non-essential messages then I think aggregation or economic disincentives will need to be put in place. HRMP should necessarily be expensive; it's mostly there to give a feel for the (XCMP) product before the product is actually ready. It's not really meant for production usage. |
||
| `send_downward_messages` can fail refusing enqueuing a message that would have exceeded the limit. In those cases | ||
| `has_dmq_capacity_for_relay_chain` can be used for checking in advance if there is enough space for a given | ||
| number of messages. | ||
|
|
||
| Note that an HRMP message can only be sent by para candidates. | ||
|
|
||
| * `has_dmq_capacity_for_relay_chain(recipient: ParaId, n: u32)`. | ||
| 1. Checks that the sum of the number `RelayChainDownwardMessages` for `recipient` and `n` is less | ||
| than or equal to `config.max_relay_chain_downward_messages`. | ||
| * `send_downward_messages(recipient: ParaId, Vec<DownwardMessage>)`. | ||
| 1. Checks that there is enough capacity in the receipient's downward queue using `has_dmq_capacity_for_relay_chain`. | ||
| 1. For each downward message `DM`: | ||
| 1. Checks that `DM` is not of type `HorizontalMessage`. | ||
| 1. Appends `DM` into the `DownwardMessageQueues` corresponding to `recipient`. | ||
| 1. Increments `RelayChainDownwardMessages` for the `recipient` according to the number of messages sent. | ||
|
|
||
| The following routines are intended for use during the course of inclusion or enactment of para candidates. | ||
| For checking the validity of message passing within a candidate the `ensure_processed_downward_messages` | ||
| and `ensure_horizontal_messages_fit` routines are called. When a candidate is enacted the | ||
| `drain_downward_messages`, `queue_horizontal_messages` and `queue_upward_messages` are called. | ||
|
|
||
| * `ensure_processed_downward_messages(recipient: ParaId, processed_downward_messages: u32)`: | ||
| 1. Checks that `DownwardMessageQueues` for `recipient` is at least `processed_downward_messages` long. | ||
| 1. Checks that `processed_downward_messages` is at least 1 if `DownwardMessageQueues` for `recipient` is not empty. | ||
| * `ensure_horizontal_messages_fit(sender, Vec<HorizontalMessage>)`: | ||
| 1. For each horizontal message `HM`, with recipient `R`: | ||
| 1. Fetches the current usage level for the pair `(sender, R)`. The usage level is defined by | ||
| a tuple of `(msg_count, total_byte_size)`. | ||
| 1. Checks that `msg_count + 1` is less or equal than `config.max_hrmp_queue_count_per_sender`. | ||
| 1. Checks that the sum of the payload size occupied by `HM` and `total_byte_size` is less than or | ||
| equal to `config.max_hrmp_queue_size_per_sender`. | ||
| * `drain_downward_messages(recipient: ParaId, processed_downward_messages)`: | ||
| 1. Prunes `processed_downward_messages` from the beginning of the downward message queue. For each pruned message `DM`: | ||
| 1. If `DM` is a horizontal message sent from a sender `S`, | ||
| 1. With the mapping from `HorizontalMessagesResourceUsage` that corresponds to `(S, recipient)` represented by | ||
| `(msg_count, total_byte_size)`. | ||
| 1. Decrements `msg_count` by 1. | ||
| 1. Decrements `total_byte_size` according to the payload size of `DM`. | ||
| 1. Otherwise, decrements `RelayChainDownwardMessages` for the `recipient`. | ||
| * `queue_horizontal_messages(sender: ParaId, Vec<HorizontalMessage>)`: | ||
| 1. For each horizontal message `HM`, with recipient `R`: | ||
| 1. Using the payload from `HM` and the `sender` creates a downward message `DM`. | ||
| 1. Appends `DM` into the `DownwardMessageQueues` corresponding to `R`. | ||
| 1. With the mapping from `HorizontalMessagesResourceUsage` that corresponds to `(sender, R)` represented by | ||
| `(msg_count, total_byte_size)`. | ||
| 1. Increment `msg_count` by 1. | ||
| 1. Increment `total_byte_size` according to the payload size of `DM`. | ||
| * `queue_upward_messages(ParaId, Vec<UpwardMessage>)`: | ||
| 1. Updates `NeedsDispatch`, and enqueues upward messages into `RelayDispatchQueue` and modifies the respective entry in `RelayDispatchQueueSize`. | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,9 @@ | |
|
|
||
| Types of messages that are passed between parachains and the relay chain: UMP, DMP, XCMP. | ||
|
|
||
| There is also HRMP (Horizontally Relay-routed Message Passing) which provides the same functionality | ||
| although with smaller scalability potential. | ||
|
|
||
| ## Upward Message | ||
|
|
||
| A type of messages dispatched from a parachain to the relay chain. | ||
|
|
@@ -26,3 +29,38 @@ struct UpwardMessage { | |
| pub data: Vec<u8>, | ||
| } | ||
| ``` | ||
|
|
||
| ## Horizontal Message | ||
|
|
||
| This is a message sent from a parachain to another parachain that travels through the relay chain. | ||
| This message ends up in the recipient's mailbox. A size of a horizontal message is defined by its | ||
| `data` payload. | ||
|
|
||
| ```rust,ignore | ||
| struct HorizontalMessage { | ||
| /// The para that will get this message in its downward message queue. | ||
| pub recipient: ParaId, | ||
| /// The message payload. | ||
| pub data: Vec<u8>, | ||
| } | ||
| ``` | ||
|
|
||
| ## Downward Message | ||
|
|
||
| A message that go down from the relay chain to a parachain. Such a message could be initiated either | ||
| as a result of an operation took place on the relay chain or sent using a horizontal message. | ||
|
|
||
| ```rust,ignore | ||
| enum DownwardMessage { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, got it. Added it below, although I couldn't help myself to choose the name
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| /// Some funds were transferred into the parachain's account. The hash is the identifier that | ||
| /// was given with the transfer. | ||
| TransferInto(AccountId, Balance, Remark), | ||
| /// This downward message is a result of a horizontal message represented as opaque bytes sent | ||
| /// by the specified sender. | ||
| HorizontalMessage(ParaId, Vec<u8>), | ||
| /// An opaque message which interpretation is up to the recipient para. This variant ought | ||
| /// to be used as a basis for special protocols between the relay chain and, typically system, | ||
| /// paras. | ||
| ParachainSpecific(Vec<u8>), | ||
| } | ||
| ``` | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
defined twice