Implement HRMP (#1900)

* HRMP: Update the impl guide

* HRMP: Incorporate the channel notifications into the guide

* HRMP: Renaming in the impl guide

* HRMP: Constrain the maximum number of HRMP messages per candidate

This commit addresses the HRMP part of https://github.com/paritytech/polkadot/issues/1869

* XCM: Introduce HRMP related message types

* HRMP: Data structures and plumbing

* HRMP: Configuration

* HRMP: Data layout

* HRMP: Acceptance & Enactment

* HRMP: Test base logic

* Update adder collator

* HRMP: Runtime API for accessing inbound messages

Also, removing some redundant fully-qualified names.

* HRMP: Add diagnostic logging in acceptance criteria

* HRMP: Additional tests

* Self-review fixes

* save test refactorings for the next time

* Missed a return statement.

* a formatting blip

* Add missing logic for appending HRMP digests

* Remove the channel contents vectors which became empty

* Tighten HRMP channel digests invariants.

* Apply suggestions from code review

Co-authored-by: Peter Goodspeed-Niklaus <coriolinus@users.noreply.github.com>

* Remove a note about sorting for channel id

* Add missing rustdocs to the configuration

* Clarify and update the invariant for HrmpChannelDigests

* Make the onboarding invariant less sloppy

Namely, introduce `Paras::is_valid_para` (in fact, it already is present
in the implementation) and hook up the invariant to that.

Note that this says "within a session" because I don't want to make it
super strict on the session boundary. The logic on the session boundary
should be extremely careful.

* Make `CandidateCheckContext` use T::BlockNumber for hrmp_watermark

Co-authored-by: Peter Goodspeed-Niklaus <coriolinus@users.noreply.github.com>
This commit is contained in:
Sergei Shulepov
2020-11-06 16:35:36 +01:00
committed by GitHub
parent 8a2911b85d
commit c96f8cfcca
33 changed files with 2024 additions and 72 deletions
+2
View File
@@ -33,6 +33,7 @@ pallet-vesting = { git = "https://github.com/paritytech/substrate", branch = "ma
pallet-offences = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true }
xcm = { package = "xcm", path = "../../xcm", default-features = false }
primitives = { package = "polkadot-primitives", path = "../../primitives", default-features = false }
libsecp256k1 = { version = "0.3.2", default-features = false, optional = true }
@@ -81,6 +82,7 @@ std = [
"frame-system/std",
"pallet-timestamp/std",
"pallet-vesting/std",
"xcm/std",
]
runtime-benchmarks = [
"libsecp256k1/hmac",
@@ -19,7 +19,7 @@
//! Configuration can change only at session boundaries and is buffered until then.
use sp_std::prelude::*;
use primitives::v1::ValidatorId;
use primitives::v1::{Balance, ValidatorId};
use frame_support::{
decl_storage, decl_module, decl_error,
dispatch::DispatchResult,
@@ -84,6 +84,32 @@ pub struct HostConfiguration<BlockNumber> {
///
/// This parameter affects the size upper bound of the `CandidateCommitments`.
pub max_upward_message_num_per_candidate: u32,
/// Number of sessions after which an HRMP open channel request expires.
pub hrmp_open_request_ttl: u32,
/// The deposit that the sender should provide for opening an HRMP channel.
pub hrmp_sender_deposit: Balance,
/// The deposit that the recipient should provide for accepting opening an HRMP channel.
pub hrmp_recipient_deposit: Balance,
/// The maximum number of messages allowed in an HRMP channel at once.
pub hrmp_channel_max_capacity: u32,
/// The maximum total size of messages in bytes allowed in an HRMP channel at once.
pub hrmp_channel_max_total_size: u32,
/// The maximum number of inbound HRMP channels a parachain is allowed to accept.
pub hrmp_max_parachain_inbound_channels: u32,
/// The maximum number of inbound HRMP channels a parathread is allowed to accept.
pub hrmp_max_parathread_inbound_channels: u32,
/// The maximum size of a message that could ever be put into an HRMP channel.
///
/// This parameter affects the upper bound of size of `CandidateCommitments`.
pub hrmp_channel_max_message_size: u32,
/// The maximum number of outbound HRMP channels a parachain is allowed to open.
pub hrmp_max_parachain_outbound_channels: u32,
/// The maximum number of outbound HRMP channels a parathread is allowed to open.
pub hrmp_max_parathread_outbound_channels: u32,
/// The maximum number of outbound HRMP messages can be sent by a candidate.
///
/// This parameter affects the upper bound of size of `CandidateCommitments`.
pub hrmp_max_message_num_per_candidate: u32,
}
pub trait Trait: frame_system::Trait { }
@@ -276,6 +302,117 @@ decl_module! {
});
Ok(())
}
/// Sets the number of sessions after which an HRMP open channel request expires.
#[weight = (1_000, DispatchClass::Operational)]
pub fn set_hrmp_open_request_ttl(origin, new: u32) -> DispatchResult {
ensure_root(origin)?;
Self::update_config_member(|config| {
sp_std::mem::replace(&mut config.hrmp_open_request_ttl, new) != new
});
Ok(())
}
/// Sets the amount of funds that the sender should provide for opening an HRMP channel.
#[weight = (1_000, DispatchClass::Operational)]
pub fn set_hrmp_sender_deposit(origin, new: Balance) -> DispatchResult {
ensure_root(origin)?;
Self::update_config_member(|config| {
sp_std::mem::replace(&mut config.hrmp_sender_deposit, new) != new
});
Ok(())
}
/// Sets the amount of funds that the recipient should provide for accepting opening an HRMP
/// channel.
#[weight = (1_000, DispatchClass::Operational)]
pub fn set_hrmp_recipient_deposit(origin, new: Balance) -> DispatchResult {
ensure_root(origin)?;
Self::update_config_member(|config| {
sp_std::mem::replace(&mut config.hrmp_recipient_deposit, new) != new
});
Ok(())
}
/// Sets the maximum number of messages allowed in an HRMP channel at once.
#[weight = (1_000, DispatchClass::Operational)]
pub fn set_hrmp_channel_max_capacity(origin, new: u32) -> DispatchResult {
ensure_root(origin)?;
Self::update_config_member(|config| {
sp_std::mem::replace(&mut config.hrmp_channel_max_capacity, new) != new
});
Ok(())
}
/// Sets the maximum total size of messages in bytes allowed in an HRMP channel at once.
#[weight = (1_000, DispatchClass::Operational)]
pub fn set_hrmp_channel_max_total_size(origin, new: u32) -> DispatchResult {
ensure_root(origin)?;
Self::update_config_member(|config| {
sp_std::mem::replace(&mut config.hrmp_channel_max_total_size, new) != new
});
Ok(())
}
/// Sets the maximum number of inbound HRMP channels a parachain is allowed to accept.
#[weight = (1_000, DispatchClass::Operational)]
pub fn set_hrmp_max_parachain_inbound_channels(origin, new: u32) -> DispatchResult {
ensure_root(origin)?;
Self::update_config_member(|config| {
sp_std::mem::replace(&mut config.hrmp_max_parachain_inbound_channels, new) != new
});
Ok(())
}
/// Sets the maximum number of inbound HRMP channels a parathread is allowed to accept.
#[weight = (1_000, DispatchClass::Operational)]
pub fn set_hrmp_max_parathread_inbound_channels(origin, new: u32) -> DispatchResult {
ensure_root(origin)?;
Self::update_config_member(|config| {
sp_std::mem::replace(&mut config.hrmp_max_parathread_inbound_channels, new) != new
});
Ok(())
}
/// Sets the maximum size of a message that could ever be put into an HRMP channel.
#[weight = (1_000, DispatchClass::Operational)]
pub fn set_hrmp_channel_max_message_size(origin, new: u32) -> DispatchResult {
ensure_root(origin)?;
Self::update_config_member(|config| {
sp_std::mem::replace(&mut config.hrmp_channel_max_message_size, new) != new
});
Ok(())
}
/// Sets the maximum number of outbound HRMP channels a parachain is allowed to open.
#[weight = (1_000, DispatchClass::Operational)]
pub fn set_hrmp_max_parachain_outbound_channels(origin, new: u32) -> DispatchResult {
ensure_root(origin)?;
Self::update_config_member(|config| {
sp_std::mem::replace(&mut config.hrmp_max_parachain_outbound_channels, new) != new
});
Ok(())
}
/// Sets the maximum number of outbound HRMP channels a parathread is allowed to open.
#[weight = (1_000, DispatchClass::Operational)]
pub fn set_hrmp_max_parathread_outbound_channels(origin, new: u32) -> DispatchResult {
ensure_root(origin)?;
Self::update_config_member(|config| {
sp_std::mem::replace(&mut config.hrmp_max_parathread_outbound_channels, new) != new
});
Ok(())
}
/// Sets the maximum number of outbound HRMP messages can be sent by a candidate.
#[weight = (1_000, DispatchClass::Operational)]
pub fn set_hrmp_max_message_num_per_candidate(origin, new: u32) -> DispatchResult {
ensure_root(origin)?;
Self::update_config_member(|config| {
sp_std::mem::replace(&mut config.hrmp_max_message_num_per_candidate, new) != new
});
Ok(())
}
}
}
@@ -360,6 +497,17 @@ mod tests {
preferred_dispatchable_upward_messages_step_weight: 20000,
max_upward_message_size: 448,
max_upward_message_num_per_candidate: 5,
hrmp_open_request_ttl: 1312,
hrmp_sender_deposit: 22,
hrmp_recipient_deposit: 4905,
hrmp_channel_max_capacity: 3921,
hrmp_channel_max_total_size: 7687,
hrmp_max_parachain_inbound_channels: 3722,
hrmp_max_parathread_inbound_channels: 1967,
hrmp_channel_max_message_size: 8192,
hrmp_max_parachain_outbound_channels: 100,
hrmp_max_parathread_outbound_channels: 200,
hrmp_max_message_num_per_candidate: 20,
};
assert!(<Configuration as Store>::PendingConfig::get().is_none());
@@ -415,6 +563,50 @@ mod tests {
Configuration::set_max_upward_message_num_per_candidate(
Origin::root(), new_config.max_upward_message_num_per_candidate,
).unwrap();
Configuration::set_hrmp_open_request_ttl(
Origin::root(),
new_config.hrmp_open_request_ttl,
).unwrap();
Configuration::set_hrmp_sender_deposit(
Origin::root(),
new_config.hrmp_sender_deposit,
).unwrap();
Configuration::set_hrmp_recipient_deposit(
Origin::root(),
new_config.hrmp_recipient_deposit,
).unwrap();
Configuration::set_hrmp_channel_max_capacity(
Origin::root(),
new_config.hrmp_channel_max_capacity,
).unwrap();
Configuration::set_hrmp_channel_max_total_size(
Origin::root(),
new_config.hrmp_channel_max_total_size,
).unwrap();
Configuration::set_hrmp_max_parachain_inbound_channels(
Origin::root(),
new_config.hrmp_max_parachain_inbound_channels,
).unwrap();
Configuration::set_hrmp_max_parathread_inbound_channels(
Origin::root(),
new_config.hrmp_max_parathread_inbound_channels,
).unwrap();
Configuration::set_hrmp_channel_max_message_size(
Origin::root(),
new_config.hrmp_channel_max_message_size,
).unwrap();
Configuration::set_hrmp_max_parachain_outbound_channels(
Origin::root(),
new_config.hrmp_max_parachain_outbound_channels,
).unwrap();
Configuration::set_hrmp_max_parathread_outbound_channels(
Origin::root(),
new_config.hrmp_max_parathread_outbound_channels,
).unwrap();
Configuration::set_hrmp_max_message_num_per_candidate(
Origin::root(),
new_config.hrmp_max_message_num_per_candidate,
).unwrap();
assert_eq!(<Configuration as Store>::PendingConfig::get(), Some(new_config));
})
@@ -157,6 +157,10 @@ decl_error! {
IncorrectDownwardMessageHandling,
/// At least one upward message sent does not pass the acceptance criteria.
InvalidUpwardMessages,
/// The candidate didn't follow the rules of HRMP watermark advancement.
HrmpWatermarkMishandling,
/// The HRMP messages sent by the candidate is not valid.
InvalidOutboundHrmp,
}
}
@@ -415,6 +419,8 @@ impl<T: Trait> Module<T> {
&candidate.candidate.commitments.new_validation_code,
candidate.candidate.commitments.processed_downward_messages,
&candidate.candidate.commitments.upward_messages,
T::BlockNumber::from(candidate.candidate.commitments.hrmp_watermark),
&candidate.candidate.commitments.horizontal_messages,
)?;
for (i, assignment) in scheduled[skip..].iter().enumerate() {
@@ -548,6 +554,8 @@ impl<T: Trait> Module<T> {
&validation_outputs.new_validation_code,
validation_outputs.processed_downward_messages,
&validation_outputs.upward_messages,
T::BlockNumber::from(validation_outputs.hrmp_watermark),
&validation_outputs.horizontal_messages,
)
}
@@ -578,6 +586,14 @@ impl<T: Trait> Module<T> {
receipt.descriptor.para_id,
commitments.upward_messages,
);
weight += <router::Module<T>>::prune_hrmp(
receipt.descriptor.para_id,
T::BlockNumber::from(commitments.hrmp_watermark),
);
weight += <router::Module<T>>::queue_outbound_hrmp(
receipt.descriptor.para_id,
commitments.horizontal_messages,
);
Self::deposit_event(
Event::<T>::CandidateIncluded(plain, commitments.head_data.clone())
@@ -702,6 +718,8 @@ impl<T: Trait> CandidateCheckContext<T> {
new_validation_code: &Option<primitives::v1::ValidationCode>,
processed_downward_messages: u32,
upward_messages: &[primitives::v1::UpwardMessage],
hrmp_watermark: T::BlockNumber,
horizontal_messages: &[primitives::v1::OutboundHrmpMessage<ParaId>],
) -> Result<(), DispatchError> {
ensure!(
head_data.0.len() <= self.config.max_head_data_size as _,
@@ -739,6 +757,22 @@ impl<T: Trait> CandidateCheckContext<T> {
),
Error::<T>::InvalidUpwardMessages,
);
ensure!(
<router::Module<T>>::check_hrmp_watermark(
para_id,
self.relay_parent_number,
hrmp_watermark,
),
Error::<T>::HrmpWatermarkMishandling,
);
ensure!(
<router::Module<T>>::check_outbound_hrmp(
&self.config,
para_id,
horizontal_messages,
),
Error::<T>::InvalidOutboundHrmp,
);
Ok(())
}
@@ -946,6 +980,7 @@ mod tests {
relay_parent: Hash,
persisted_validation_data_hash: Hash,
new_validation_code: Option<ValidationCode>,
hrmp_watermark: BlockNumber,
}
impl TestCandidateBuilder {
@@ -961,6 +996,7 @@ mod tests {
commitments: CandidateCommitments {
head_data: self.head_data,
new_validation_code: self.new_validation_code,
hrmp_watermark: self.hrmp_watermark,
..Default::default()
},
}
@@ -1359,6 +1395,9 @@ mod tests {
let chain_b = ParaId::from(2);
let thread_a = ParaId::from(3);
// The block number of the relay-parent for testing.
const RELAY_PARENT_NUM: BlockNumber = 4;
let paras = vec![(chain_a, true), (chain_b, true), (thread_a, false)];
let validators = vec![
Sr25519Keyring::Alice,
@@ -1421,6 +1460,7 @@ mod tests {
relay_parent: System::parent_hash(),
pov_hash: Hash::from([1; 32]),
persisted_validation_data_hash: make_vdata_hash(chain_a).unwrap(),
hrmp_watermark: RELAY_PARENT_NUM,
..Default::default()
}.build();
collator_sign_candidate(
@@ -1454,6 +1494,7 @@ mod tests {
relay_parent: System::parent_hash(),
pov_hash: Hash::from([1; 32]),
persisted_validation_data_hash: make_vdata_hash(chain_a).unwrap(),
hrmp_watermark: RELAY_PARENT_NUM,
..Default::default()
}.build();
let mut candidate_b = TestCandidateBuilder {
@@ -1461,6 +1502,7 @@ mod tests {
relay_parent: System::parent_hash(),
pov_hash: Hash::from([2; 32]),
persisted_validation_data_hash: make_vdata_hash(chain_b).unwrap(),
hrmp_watermark: RELAY_PARENT_NUM,
..Default::default()
}.build();
@@ -1510,6 +1552,7 @@ mod tests {
relay_parent: System::parent_hash(),
pov_hash: Hash::from([1; 32]),
persisted_validation_data_hash: make_vdata_hash(chain_a).unwrap(),
hrmp_watermark: RELAY_PARENT_NUM,
..Default::default()
}.build();
collator_sign_candidate(
@@ -1579,6 +1622,7 @@ mod tests {
relay_parent: System::parent_hash(),
pov_hash: Hash::from([1; 32]),
persisted_validation_data_hash: make_vdata_hash(thread_a).unwrap(),
hrmp_watermark: RELAY_PARENT_NUM,
..Default::default()
}.build();
@@ -1618,6 +1662,7 @@ mod tests {
relay_parent: System::parent_hash(),
pov_hash: Hash::from([1; 32]),
persisted_validation_data_hash: make_vdata_hash(thread_a).unwrap(),
hrmp_watermark: RELAY_PARENT_NUM,
..Default::default()
}.build();
@@ -1656,6 +1701,7 @@ mod tests {
relay_parent: System::parent_hash(),
pov_hash: Hash::from([1; 32]),
persisted_validation_data_hash: make_vdata_hash(chain_a).unwrap(),
hrmp_watermark: RELAY_PARENT_NUM,
..Default::default()
}.build();
@@ -1703,6 +1749,7 @@ mod tests {
relay_parent: System::parent_hash(),
pov_hash: Hash::from([1; 32]),
persisted_validation_data_hash: make_vdata_hash(chain_a).unwrap(),
hrmp_watermark: RELAY_PARENT_NUM,
..Default::default()
}.build();
@@ -1743,6 +1790,7 @@ mod tests {
pov_hash: Hash::from([1; 32]),
new_validation_code: Some(vec![5, 6, 7, 8].into()),
persisted_validation_data_hash: make_vdata_hash(chain_a).unwrap(),
hrmp_watermark: RELAY_PARENT_NUM,
..Default::default()
}.build();
@@ -1785,6 +1833,7 @@ mod tests {
relay_parent: System::parent_hash(),
pov_hash: Hash::from([1; 32]),
persisted_validation_data_hash: [42u8; 32].into(),
hrmp_watermark: RELAY_PARENT_NUM,
..Default::default()
}.build();
@@ -1820,6 +1869,9 @@ mod tests {
let chain_b = ParaId::from(2);
let thread_a = ParaId::from(3);
// The block number of the relay-parent for testing.
const RELAY_PARENT_NUM: BlockNumber = 4;
let paras = vec![(chain_a, true), (chain_b, true), (thread_a, false)];
let validators = vec![
Sr25519Keyring::Alice,
@@ -1880,6 +1932,7 @@ mod tests {
relay_parent: System::parent_hash(),
pov_hash: Hash::from([1; 32]),
persisted_validation_data_hash: make_vdata_hash(chain_a).unwrap(),
hrmp_watermark: RELAY_PARENT_NUM,
..Default::default()
}.build();
collator_sign_candidate(
@@ -1892,6 +1945,7 @@ mod tests {
relay_parent: System::parent_hash(),
pov_hash: Hash::from([2; 32]),
persisted_validation_data_hash: make_vdata_hash(chain_b).unwrap(),
hrmp_watermark: RELAY_PARENT_NUM,
..Default::default()
}.build();
collator_sign_candidate(
@@ -1904,6 +1958,7 @@ mod tests {
relay_parent: System::parent_hash(),
pov_hash: Hash::from([3; 32]),
persisted_validation_data_hash: make_vdata_hash(thread_a).unwrap(),
hrmp_watermark: RELAY_PARENT_NUM,
..Default::default()
}.build();
collator_sign_candidate(
@@ -2001,6 +2056,9 @@ mod tests {
fn can_include_candidate_with_ok_code_upgrade() {
let chain_a = ParaId::from(1);
// The block number of the relay-parent for testing.
const RELAY_PARENT_NUM: BlockNumber = 4;
let paras = vec![(chain_a, true)];
let validators = vec![
Sr25519Keyring::Alice,
@@ -2044,6 +2102,7 @@ mod tests {
pov_hash: Hash::from([1; 32]),
persisted_validation_data_hash: make_vdata_hash(chain_a).unwrap(),
new_validation_code: Some(vec![1, 2, 3].into()),
hrmp_watermark: RELAY_PARENT_NUM,
..Default::default()
}.build();
collator_sign_candidate(
+1
View File
@@ -109,6 +109,7 @@ impl crate::paras::Trait for Test {
}
impl crate::router::Trait for Test {
type Origin = Origin;
type UmpSink = crate::router::MockUmpSink;
}
+6
View File
@@ -541,6 +541,12 @@ impl<T: Trait> Module<T> {
}
}
/// Returns whether the given ID refers to a valid para.
pub(crate) fn is_valid_para(id: ParaId) -> bool {
Self::parachains().binary_search(&id).is_ok()
|| Self::is_parathread(id)
}
/// Whether a para ID corresponds to any live parathread.
pub(crate) fn is_parathread(id: ParaId) -> bool {
Parathreads::get(&id).is_some()
+154 -9
View File
@@ -20,25 +20,30 @@
//! routing the messages at their destinations and informing the parachains about the incoming
//! messages.
use crate::{
configuration,
initializer,
};
use crate::{configuration, paras, initializer, ensure_parachain};
use sp_std::prelude::*;
use frame_support::{decl_error, decl_module, decl_storage, weights::Weight};
use frame_support::{decl_error, decl_module, decl_storage, dispatch::DispatchResult, weights::Weight};
use sp_std::collections::vec_deque::VecDeque;
use primitives::v1::{Id as ParaId, InboundDownwardMessage, Hash, UpwardMessage};
use primitives::v1::{
Id as ParaId, InboundDownwardMessage, Hash, UpwardMessage, HrmpChannelId, InboundHrmpMessage,
};
mod dmp;
mod hrmp;
mod ump;
use hrmp::{HrmpOpenChannelRequest, HrmpChannel};
pub use dmp::QueueDownwardMessageError;
pub use ump::UmpSink;
#[cfg(test)]
pub use ump::mock_sink::MockUmpSink;
pub trait Trait: frame_system::Trait + configuration::Trait {
pub trait Trait: frame_system::Trait + configuration::Trait + paras::Trait {
type Origin: From<crate::Origin>
+ From<<Self as frame_system::Trait>::Origin>
+ Into<Result<crate::Origin, <Self as Trait>::Origin>>;
/// A place where all received upward messages are funneled.
type UmpSink: UmpSink;
}
@@ -103,17 +108,148 @@ decl_storage! {
/// Invariant:
/// - If `Some(para)`, then `para` must be present in `NeedsDispatch`.
NextDispatchRoundStartWith: Option<ParaId>;
/*
* Horizontally Relay-routed Message Passing (HRMP)
*
* HRMP related storage layout
*/
/// 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 hasher(twox_64_concat) HrmpChannelId => Option<HrmpOpenChannelRequest>;
HrmpOpenChannelRequestsList: Vec<HrmpChannelId>;
/// 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 `(X, _)`
/// as the number of `HrmpOpenChannelRequestCount` for `X`.
HrmpOpenChannelRequestCount: map hasher(twox_64_concat) ParaId => u32;
/// This mapping tracks how many open channel requests were accepted by a given recipient para.
/// Invariant: `HrmpOpenChannelRequests` should contain the same number of items `(_, X)` with
/// `confirmed` set to true, as the number of `HrmpAcceptedChannelRequestCount` for `X`.
HrmpAcceptedChannelRequestCount: map hasher(twox_64_concat) ParaId => u32;
/// 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 hasher(twox_64_concat) HrmpChannelId => Option<()>;
HrmpCloseChannelRequestsList: Vec<HrmpChannelId>;
/// The HRMP watermark associated with each para.
/// Invariant:
/// - each para `P` used here as a key should satisfy `Paras::is_valid_para(P)` within a session.
HrmpWatermarks: map hasher(twox_64_concat) ParaId => Option<T::BlockNumber>;
/// HRMP channel data associated with each para.
/// Invariant:
/// - each participant in the channel should satisfy `Paras::is_valid_para(P)` within a session.
HrmpChannels: map hasher(twox_64_concat) HrmpChannelId => Option<HrmpChannel>;
/// Ingress/egress indexes allow to find all the senders and receivers given the opposite
/// side. I.e.
///
/// (a) ingress index allows to find all the senders for a given recipient.
/// (b) egress index allows to find all the recipients for a given sender.
///
/// Invariants:
/// - for each ingress index entry for `P` each item `I` in the index should present in `HrmpChannels`
/// as `(I, P)`.
/// - for each egress index entry for `P` each item `E` in the index should present in `HrmpChannels`
/// as `(P, E)`.
/// - there should be no other dangling channels in `HrmpChannels`.
/// - the vectors are sorted.
HrmpIngressChannelsIndex: map hasher(twox_64_concat) ParaId => Vec<ParaId>;
HrmpEgressChannelsIndex: map hasher(twox_64_concat) ParaId => Vec<ParaId>;
/// Storage for the messages for each channel.
/// Invariant: cannot be non-empty if the corresponding channel in `HrmpChannels` is `None`.
HrmpChannelContents: map hasher(twox_64_concat) HrmpChannelId => Vec<InboundHrmpMessage<T::BlockNumber>>;
/// Maintains a mapping that can be used to answer the question:
/// What paras sent a message at the given block number for a given reciever.
/// Invariants:
/// - The inner `Vec<ParaId>` is never empty.
/// - The inner `Vec<ParaId>` cannot store two same `ParaId`.
/// - The outer vector is sorted ascending by block number and cannot store two items with the same
/// block number.
HrmpChannelDigests: map hasher(twox_64_concat) ParaId => Vec<(T::BlockNumber, Vec<ParaId>)>;
}
}
decl_error! {
pub enum Error for Module<T: Trait> { }
pub enum Error for Module<T: Trait> {
/// The sender tried to open a channel to themselves.
OpenHrmpChannelToSelf,
/// The recipient is not a valid para.
OpenHrmpChannelInvalidRecipient,
/// The requested capacity is zero.
OpenHrmpChannelZeroCapacity,
/// The requested capacity exceeds the global limit.
OpenHrmpChannelCapacityExceedsLimit,
/// The requested maximum message size is 0.
OpenHrmpChannelZeroMessageSize,
/// The open request requested the message size that exceeds the global limit.
OpenHrmpChannelMessageSizeExceedsLimit,
/// The channel already exists
OpenHrmpChannelAlreadyExists,
/// There is already a request to open the same channel.
OpenHrmpChannelAlreadyRequested,
/// The sender already has the maximum number of allowed outbound channels.
OpenHrmpChannelLimitExceeded,
/// The channel from the sender to the origin doesn't exist.
AcceptHrmpChannelDoesntExist,
/// The channel is already confirmed.
AcceptHrmpChannelAlreadyConfirmed,
/// The recipient already has the maximum number of allowed inbound channels.
AcceptHrmpChannelLimitExceeded,
/// The origin tries to close a channel where it is neither the sender nor the recipient.
CloseHrmpChannelUnauthorized,
/// The channel to be closed doesn't exist.
CloseHrmpChannelDoesntExist,
/// The channel close request is already requested.
CloseHrmpChannelAlreadyUnderway,
}
}
decl_module! {
/// The router module.
pub struct Module<T: Trait> for enum Call where origin: <T as frame_system::Trait>::Origin {
type Error = Error<T>;
#[weight = 0]
fn hrmp_init_open_channel(
origin,
recipient: ParaId,
proposed_max_capacity: u32,
proposed_max_message_size: u32,
) -> DispatchResult {
let origin = ensure_parachain(<T as Trait>::Origin::from(origin))?;
Self::init_open_channel(
origin,
recipient,
proposed_max_capacity,
proposed_max_message_size
)?;
Ok(())
}
#[weight = 0]
fn hrmp_accept_open_channel(origin, sender: ParaId) -> DispatchResult {
let origin = ensure_parachain(<T as Trait>::Origin::from(origin))?;
Self::accept_open_channel(origin, sender)?;
Ok(())
}
#[weight = 0]
fn hrmp_close_channel(origin, channel_id: HrmpChannelId) -> DispatchResult {
let origin = ensure_parachain(<T as Trait>::Origin::from(origin))?;
Self::close_channel(origin, channel_id)?;
Ok(())
}
}
}
@@ -128,12 +264,21 @@ impl<T: Trait> Module<T> {
/// Called by the initializer to note that a new session has started.
pub(crate) fn initializer_on_new_session(
_notification: &initializer::SessionChangeNotification<T::BlockNumber>,
notification: &initializer::SessionChangeNotification<T::BlockNumber>,
) {
Self::perform_outgoing_para_cleanup();
Self::process_hrmp_open_channel_requests(&notification.prev_config);
Self::process_hrmp_close_channel_requests();
}
/// Iterate over all paras that were registered for offboarding and remove all the data
/// associated with them.
fn perform_outgoing_para_cleanup() {
let outgoing = OutgoingParas::take();
for outgoing_para in outgoing {
Self::clean_dmp_after_outgoing(outgoing_para);
Self::clean_ump_after_outgoing(outgoing_para);
Self::clean_hrmp_after_outgoing(outgoing_para);
}
}
File diff suppressed because it is too large Load Diff
@@ -18,12 +18,13 @@
//! functions.
use sp_std::prelude::*;
use sp_std::collections::btree_map::BTreeMap;
use primitives::v1::{
ValidatorId, ValidatorIndex, GroupRotationInfo, CoreState, ValidationData,
Id as ParaId, OccupiedCoreAssumption, SessionIndex, ValidationCode,
CommittedCandidateReceipt, ScheduledCore, OccupiedCore, CoreOccupied, CoreIndex,
GroupIndex, CandidateEvent, PersistedValidationData, AuthorityDiscoveryId,
InboundDownwardMessage,
InboundDownwardMessage, InboundHrmpMessage,
};
use sp_runtime::traits::Zero;
use frame_support::debug;
@@ -328,3 +329,10 @@ pub fn dmq_contents<T: router::Trait>(
) -> Vec<InboundDownwardMessage<T::BlockNumber>> {
<router::Module<T>>::dmq_contents(recipient)
}
/// Implementation for the `inbound_hrmp_channels_contents` function of the runtime API.
pub fn inbound_hrmp_channels_contents<T: router::Trait>(
recipient: ParaId,
) -> BTreeMap<ParaId, Vec<InboundHrmpMessage<T::BlockNumber>>> {
<router::Module<T>>::inbound_hrmp_channels_contents(recipient)
}
+1 -2
View File
@@ -19,7 +19,6 @@
use sp_runtime::traits::{One, Saturating};
use primitives::v1::{Id as ParaId, PersistedValidationData, TransientValidationData};
use sp_std::prelude::*;
use crate::{configuration, paras, router};
@@ -34,7 +33,7 @@ pub fn make_persisted_validation_data<T: paras::Trait + router::Trait>(
Some(PersistedValidationData {
parent_head: <paras::Module<T>>::para_head(&para_id)?,
block_number: relay_parent_number,
hrmp_mqc_heads: Vec::new(),
hrmp_mqc_heads: <router::Module<T>>::hrmp_mqc_heads(para_id),
dmq_mqc_head: <router::Module<T>>::dmq_mqc_head(para_id),
})
}