Parachains hrmp.rs to Frame V2 (#3475)

* migrate

* migrate runtimes

* runtimes compile

* ci

* trigger ci
This commit is contained in:
ferrell-code
2021-07-22 06:21:22 -04:00
committed by GitHub
parent 4bb487985a
commit 26330640d2
9 changed files with 253 additions and 181 deletions
+239 -167
View File
@@ -20,11 +20,9 @@ use crate::{
initializer, paras, dmp,
};
use parity_scale_codec::{Decode, Encode};
use frame_support::{
decl_storage, decl_module, decl_error, decl_event, ensure, traits::{Get, ReservableCurrency},
weights::Weight, StorageMap, StorageValue, dispatch::DispatchResult,
};
use frame_system::ensure_root;
use frame_support::pallet_prelude::*;
use frame_support::traits::ReservableCurrency;
use frame_system::pallet_prelude::*;
use primitives::v1::{
Balance, Hash, HrmpChannelId, Id as ParaId, InboundHrmpMessage, OutboundHrmpMessage,
SessionIndex,
@@ -36,6 +34,8 @@ use sp_std::{
prelude::*,
};
pub use pallet::*;
/// A description of a request to open an HRMP channel.
#[derive(Encode, Decode)]
pub struct HrmpOpenChannelRequest {
@@ -215,139 +215,46 @@ impl fmt::Debug for OutboundHrmpAcceptanceErr {
}
}
pub trait Config: frame_system::Config + configuration::Config + paras::Config + dmp::Config {
/// The outer event type.
type Event: From<Event> + Into<<Self as frame_system::Config>::Event>;
#[frame_support::pallet]
pub mod pallet {
use super::*;
type Origin: From<crate::Origin>
+ From<<Self as frame_system::Config>::Origin>
+ Into<Result<crate::Origin, <Self as Config>::Origin>>;
/// An interface for reserving deposits for opening channels.
///
/// NOTE that this Currency instance will be charged with the amounts defined in the `Configuration`
/// module. Specifically, that means that the `Balance` of the `Currency` implementation should
/// be the same as `Balance` as used in the `Configuration`.
type Currency: ReservableCurrency<Self::AccountId>;
}
#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
pub struct Pallet<T>(_);
decl_storage! {
trait Store for Module<T: Config> as Hrmp {
/// The set of pending HRMP open channel requests.
#[pallet::config]
pub trait Config: frame_system::Config + configuration::Config + paras::Config + dmp::Config {
/// The outer event type.
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
type Origin: From<crate::Origin>
+ From<<Self as frame_system::Config>::Origin>
+ Into<Result<crate::Origin, <Self as Config>::Origin>>;
/// An interface for reserving deposits for opening channels.
///
/// 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 initiated 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>;
// NOTE that this field is used by parachains via merkle storage proofs, therefore changing
// the format will require migration of parachains.
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 receiver.
/// 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>)>;
/// NOTE that this Currency instance will be charged with the amounts defined in the `Configuration`
/// pallet. Specifically, that means that the `Balance` of the `Currency` implementation should
/// be the same as `Balance` as used in the `Configuration`.
type Currency: ReservableCurrency<Self::AccountId>;
}
add_extra_genesis {
/// Preopen the given HRMP channels.
///
/// The values in the tuple corresponds to `(sender, recipient, max_capacity, max_message_size)`,
/// i.e. similar to `init_open_channel`. In fact, the initialization is performed as if
/// the `init_open_channel` and `accept_open_channel` were called with the respective parameters
/// and the session change take place.
///
/// As such, each channel initializer should satisfy the same constraints, namely:
///
/// 1. `max_capacity` and `max_message_size` should be within the limits set by the configuration module.
/// 2. `sender` and `recipient` must be valid paras.
config(preopen_hrmp_channels): Vec<(ParaId, ParaId, u32, u32)>;
build(|config| {
initialize_storage::<T>(&config.preopen_hrmp_channels);
})
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// Open HRMP channel requested.
/// `[sender, recipient, proposed_max_capacity, proposed_max_message_size]`
OpenChannelRequested(ParaId, ParaId, u32, u32),
/// Open HRMP channel accepted. `[sender, recipient]`
OpenChannelAccepted(ParaId, ParaId),
/// HRMP channel closed. `[by_parachain, channel_id]`
ChannelClosed(ParaId, HrmpChannelId),
}
}
#[cfg(feature = "std")]
fn initialize_storage<T: Config>(preopen_hrmp_channels: &[(ParaId, ParaId, u32, u32)]) {
let host_config = configuration::Module::<T>::config();
for &(sender, recipient, max_capacity, max_message_size) in preopen_hrmp_channels {
if let Err(err) = preopen_hrmp_channel::<T>(sender, recipient, max_capacity, max_message_size) {
panic!("failed to initialize the genesis storage: {:?}", err);
}
}
<Module<T>>::process_hrmp_open_channel_requests(&host_config);
}
#[cfg(feature = "std")]
fn preopen_hrmp_channel<T: Config>(
sender: ParaId,
recipient: ParaId,
max_capacity: u32,
max_message_size: u32
) -> DispatchResult {
<Module<T>>::init_open_channel(
sender,
recipient,
max_capacity,
max_message_size,
)?;
<Module<T>>::accept_open_channel(recipient, sender)?;
Ok(())
}
decl_error! {
pub enum Error for Module<T: Config> {
#[pallet::error]
pub enum Error<T> {
/// The sender tried to open a channel to themselves.
OpenHrmpChannelToSelf,
/// The recipient is not a valid para.
@@ -378,28 +285,168 @@ decl_error! {
CloseHrmpChannelDoesntExist,
/// The channel close request is already requested.
CloseHrmpChannelAlreadyUnderway,
}
}
decl_event! {
pub enum Event {
/// Open HRMP channel requested.
/// `[sender, recipient, proposed_max_capacity, proposed_max_message_size]`
OpenChannelRequested(ParaId, ParaId, u32, u32),
/// Open HRMP channel accepted. `[sender, recipient]`
OpenChannelAccepted(ParaId, ParaId),
/// HRMP channel closed. `[by_parachain, channel_id]`
ChannelClosed(ParaId, HrmpChannelId),
}
}
decl_module! {
/// The HRMP module.
pub struct Module<T: Config> for enum Call where origin: <T as frame_system::Config>::Origin {
type Error = Error<T>;
/// 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.
#[pallet::storage]
pub type HrmpOpenChannelRequests<T: Config> = StorageMap<
_,
Twox64Concat,
HrmpChannelId,
HrmpOpenChannelRequest
>;
fn deposit_event() = default;
#[pallet::storage]
pub type HrmpOpenChannelRequestsList<T: Config> = StorageValue<_, Vec<HrmpChannelId>, ValueQuery>;
/// This mapping tracks how many open channel requests are initiated by a given sender para.
/// Invariant: `HrmpOpenChannelRequests` should contain the same number of items that has `(X, _)`
/// as the number of `HrmpOpenChannelRequestCount` for `X`.
#[pallet::storage]
pub type HrmpOpenChannelRequestCount<T: Config> = StorageMap<
_,
Twox64Concat,
ParaId,
u32,
ValueQuery
>;
/// 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`.
#[pallet::storage]
pub type HrmpAcceptedChannelRequestCount<T: Config> = StorageMap<
_,
Twox64Concat,
ParaId,
u32,
ValueQuery
>;
/// 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.
#[pallet::storage]
pub type HrmpCloseChannelRequests<T: Config> = StorageMap<_, Twox64Concat, HrmpChannelId, ()>;
#[pallet::storage]
pub type HrmpCloseChannelRequestsList<T: Config> = StorageValue<_, Vec<HrmpChannelId>, ValueQuery>;
/// 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.
#[pallet::storage]
pub type HrmpWatermarks<T: Config> = StorageMap<_, Twox64Concat, ParaId, 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.
#[pallet::storage]
pub type HrmpChannels<T: Config> = StorageMap<_, Twox64Concat, HrmpChannelId, 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.
#[pallet::storage]
pub type HrmpIngressChannelsIndex<T: Config> = StorageMap<
_,
Twox64Concat,
ParaId,
Vec<ParaId>,
ValueQuery
>;
// NOTE that this field is used by parachains via merkle storage proofs, therefore changing
// the format will require migration of parachains.
#[pallet::storage]
pub type HrmpEgressChannelsIndex<T: Config> = StorageMap<
_,
Twox64Concat,
ParaId,
Vec<ParaId>,
ValueQuery
>;
/// Storage for the messages for each channel.
/// Invariant: cannot be non-empty if the corresponding channel in `HrmpChannels` is `None`.
#[pallet::storage]
pub type HrmpChannelContents<T: Config> = StorageMap<
_,
Twox64Concat,
HrmpChannelId,
Vec<InboundHrmpMessage<T::BlockNumber>>,
ValueQuery
>;
/// Maintains a mapping that can be used to answer the question:
/// What paras sent a message at the given block number for a given receiver.
/// 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.
#[pallet::storage]
pub type HrmpChannelDigests<T: Config> = StorageMap<
_,
Twox64Concat,
ParaId,
Vec<(T::BlockNumber, Vec<ParaId>)>,
ValueQuery
>;
/// Preopen the given HRMP channels.
///
/// The values in the tuple corresponds to `(sender, recipient, max_capacity, max_message_size)`,
/// i.e. similar to `init_open_channel`. In fact, the initialization is performed as if
/// the `init_open_channel` and `accept_open_channel` were called with the respective parameters
/// and the session change take place.
///
/// As such, each channel initializer should satisfy the same constraints, namely:
///
/// 1. `max_capacity` and `max_message_size` should be within the limits set by the configuration pallet.
/// 2. `sender` and `recipient` must be valid paras.
#[pallet::genesis_config]
pub struct GenesisConfig {
preopen_hrmp_channels: Vec<(ParaId, ParaId, u32, u32)>,
}
#[cfg(feature = "std")]
impl Default for GenesisConfig {
fn default() -> Self {
GenesisConfig {
preopen_hrmp_channels: Default::default(),
}
}
}
#[pallet::genesis_build]
impl<T: Config> GenesisBuild<T> for GenesisConfig {
fn build(&self) {
initialize_storage::<T>(&self.preopen_hrmp_channels);
}
}
#[pallet::call]
impl<T: Config> Pallet<T> {
/// Initiate opening a channel from a parachain to a given recipient with given channel
/// parameters.
///
@@ -410,9 +457,9 @@ decl_module! {
///
/// The channel can be opened only after the recipient confirms it and only on a session
/// change.
#[weight = 0]
#[pallet::weight(0)]
pub fn hrmp_init_open_channel(
origin,
origin: OriginFor<T>,
recipient: ParaId,
proposed_max_capacity: u32,
proposed_max_message_size: u32,
@@ -436,8 +483,8 @@ decl_module! {
/// Accept a pending open channel request from the given sender.
///
/// The channel will be opened only on the next session boundary.
#[weight = 0]
pub fn hrmp_accept_open_channel(origin, sender: ParaId) -> DispatchResult {
#[pallet::weight(0)]
pub fn hrmp_accept_open_channel(origin: OriginFor<T>, sender: ParaId) -> DispatchResult {
let origin = ensure_parachain(<T as Config>::Origin::from(origin))?;
Self::accept_open_channel(origin, sender)?;
Self::deposit_event(Event::OpenChannelAccepted(sender, origin));
@@ -448,8 +495,8 @@ decl_module! {
/// recipient in the channel being closed.
///
/// The closure can only happen on a session change.
#[weight = 0]
pub fn hrmp_close_channel(origin, channel_id: HrmpChannelId) -> DispatchResult {
#[pallet::weight(0)]
pub fn hrmp_close_channel(origin: OriginFor<T>, channel_id: HrmpChannelId) -> DispatchResult {
let origin = ensure_parachain(<T as Config>::Origin::from(origin))?;
Self::close_channel(origin, channel_id.clone())?;
Self::deposit_event(Event::ChannelClosed(origin, channel_id));
@@ -461,8 +508,8 @@ decl_module! {
/// you to trigger the cleanup immediately for a specific parachain.
///
/// Origin must be Root.
#[weight = 0]
pub fn force_clean_hrmp(origin, para: ParaId) -> DispatchResult {
#[pallet::weight(0)]
pub fn force_clean_hrmp(origin: OriginFor<T>, para: ParaId) -> DispatchResult {
ensure_root(origin)?;
Self::clean_hrmp_after_outgoing(&para);
Ok(())
@@ -472,8 +519,8 @@ decl_module! {
///
/// If there are pending HRMP open channel requests, you can use this
/// function process all of those requests immediately.
#[weight = 0]
pub fn force_process_hrmp_open(origin) -> DispatchResult {
#[pallet::weight(0)]
pub fn force_process_hrmp_open(origin: OriginFor<T>) -> DispatchResult {
ensure_root(origin)?;
let host_config = configuration::Module::<T>::config();
Self::process_hrmp_open_channel_requests(&host_config);
@@ -484,8 +531,8 @@ decl_module! {
///
/// If there are pending HRMP close channel requests, you can use this
/// function process all of those requests immediately.
#[weight = 0]
pub fn force_process_hrmp_close(origin) -> DispatchResult {
#[pallet::weight(0)]
pub fn force_process_hrmp_close(origin: OriginFor<T>) -> DispatchResult {
ensure_root(origin)?;
Self::process_hrmp_close_channel_requests();
Ok(())
@@ -493,8 +540,36 @@ decl_module! {
}
}
#[cfg(feature = "std")]
fn initialize_storage<T: Config>(preopen_hrmp_channels: &[(ParaId, ParaId, u32, u32)]) {
let host_config = configuration::Module::<T>::config();
for &(sender, recipient, max_capacity, max_message_size) in preopen_hrmp_channels {
if let Err(err) = preopen_hrmp_channel::<T>(sender, recipient, max_capacity, max_message_size) {
panic!("failed to initialize the genesis storage: {:?}", err);
}
}
<Pallet<T>>::process_hrmp_open_channel_requests(&host_config);
}
#[cfg(feature = "std")]
fn preopen_hrmp_channel<T: Config>(
sender: ParaId,
recipient: ParaId,
max_capacity: u32,
max_message_size: u32
) -> DispatchResult {
<Pallet<T>>::init_open_channel(
sender,
recipient,
max_capacity,
max_message_size,
)?;
<Pallet<T>>::accept_open_channel(recipient, sender)?;
Ok(())
}
/// Routines and getters related to HRMP.
impl<T: Config> Module<T> {
impl<T: Config> Pallet<T> {
/// Block initialization logic, called by initializer.
pub(crate) fn initializer_initialize(_now: T::BlockNumber) -> Weight {
0
@@ -962,7 +1037,7 @@ impl<T: Config> Module<T> {
/// Initiate opening a channel from a parachain to a given recipient with given channel
/// parameters.
///
/// Basically the same as [`hrmp_init_open_channel`](Module::hrmp_init_open_channel) but intendend for calling directly from
/// Basically the same as [`hrmp_init_open_channel`](Pallet::hrmp_init_open_channel) but intendend for calling directly from
/// other pallets rather than dispatched.
pub fn init_open_channel(
origin: ParaId,
@@ -1063,7 +1138,7 @@ impl<T: Config> Module<T> {
/// Accept a pending open channel request from the given sender.
///
/// Basically the same as [`hrmp_accept_open_channel`](Module::hrmp_accept_open_channel) but
/// Basically the same as [`hrmp_accept_open_channel`](Pallet::hrmp_accept_open_channel) but
/// intendend for calling directly from other pallets rather than dispatched.
pub fn accept_open_channel(origin: ParaId, sender: ParaId) -> DispatchResult {
let channel_id = HrmpChannelId {
@@ -1225,7 +1300,6 @@ mod tests {
use std::collections::{BTreeMap, HashSet};
fn run_to_block(to: BlockNumber, new_session: Option<Vec<BlockNumber>>) {
use frame_support::traits::{OnFinalize as _, OnInitialize as _};
let config = Configuration::config();
while System::block_number() < to {
@@ -1357,8 +1431,6 @@ mod tests {
}
fn assert_storage_consistency_exhaustive() {
use frame_support::IterableStorageMap;
assert_eq!(
<Hrmp as Store>::HrmpOpenChannelRequests::iter()
.map(|(k, _)| k)