mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-15 13:51:11 +00:00
parachain-system (#296)
* rename parachain-{upgrade -> system}
* Merge message-broker into parachain-system
* Remove message-broker and clean up
* Update docs
* Test upward messages sending
And also update the relay-sproof-builder so that it allows to set the
relay dispatch queue size for the given parachain.
* Test horizontal message sending
* Remove old inherent definitions
This commit is contained in:
Generated
+4
-17
@@ -1085,19 +1085,6 @@ dependencies = [
|
|||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cumulus-message-broker"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"cumulus-parachain-upgrade",
|
|
||||||
"cumulus-primitives",
|
|
||||||
"frame-support",
|
|
||||||
"frame-system",
|
|
||||||
"parity-scale-codec",
|
|
||||||
"sp-inherents",
|
|
||||||
"sp-std",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cumulus-network"
|
name = "cumulus-network"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -1128,7 +1115,7 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cumulus-parachain-upgrade"
|
name = "cumulus-parachain-system"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cumulus-primitives",
|
"cumulus-primitives",
|
||||||
@@ -1253,8 +1240,7 @@ dependencies = [
|
|||||||
name = "cumulus-test-parachain-runtime"
|
name = "cumulus-test-parachain-runtime"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cumulus-message-broker",
|
"cumulus-parachain-system",
|
||||||
"cumulus-parachain-upgrade",
|
|
||||||
"cumulus-primitives",
|
"cumulus-primitives",
|
||||||
"cumulus-runtime",
|
"cumulus-runtime",
|
||||||
"frame-executive",
|
"frame-executive",
|
||||||
@@ -1297,13 +1283,14 @@ dependencies = [
|
|||||||
"polkadot-primitives",
|
"polkadot-primitives",
|
||||||
"sp-runtime",
|
"sp-runtime",
|
||||||
"sp-state-machine",
|
"sp-state-machine",
|
||||||
|
"sp-std",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cumulus-test-runtime"
|
name = "cumulus-test-runtime"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cumulus-parachain-upgrade",
|
"cumulus-parachain-system",
|
||||||
"cumulus-primitives",
|
"cumulus-primitives",
|
||||||
"cumulus-runtime",
|
"cumulus-runtime",
|
||||||
"frame-executive",
|
"frame-executive",
|
||||||
|
|||||||
+1
-2
@@ -1,9 +1,8 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"consensus",
|
"consensus",
|
||||||
"message-broker",
|
|
||||||
"network",
|
"network",
|
||||||
"parachain-upgrade",
|
"parachain-system",
|
||||||
"primitives",
|
"primitives",
|
||||||
"rococo-parachains/",
|
"rococo-parachains/",
|
||||||
"rococo-parachains/pallets/parachain-info",
|
"rococo-parachains/pallets/parachain-info",
|
||||||
|
|||||||
+12
-28
@@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
use cumulus_network::WaitToAnnounce;
|
use cumulus_network::WaitToAnnounce;
|
||||||
use cumulus_primitives::{
|
use cumulus_primitives::{
|
||||||
inherents::{self, VALIDATION_DATA_IDENTIFIER},
|
inherents,
|
||||||
well_known_keys, InboundDownwardMessage, InboundHrmpMessage, OutboundHrmpMessage,
|
well_known_keys, InboundDownwardMessage, InboundHrmpMessage, OutboundHrmpMessage,
|
||||||
PersistedValidationData, relay_chain,
|
PersistedValidationData, relay_chain,
|
||||||
};
|
};
|
||||||
@@ -286,45 +286,29 @@ where
|
|||||||
})
|
})
|
||||||
.ok()?;
|
.ok()?;
|
||||||
|
|
||||||
let validation_data = {
|
let system_inherent_data = {
|
||||||
let relay_chain_state = self.collect_relay_storage_proof(relay_parent)?;
|
let relay_chain_state = self.collect_relay_storage_proof(relay_parent)?;
|
||||||
inherents::ValidationDataType {
|
let downward_messages = self.retrieve_dmq_contents(relay_parent)?;
|
||||||
|
let horizontal_messages =
|
||||||
|
self.retrieve_all_inbound_hrmp_channel_contents(relay_parent)?;
|
||||||
|
|
||||||
|
inherents::SystemInherentData {
|
||||||
|
downward_messages,
|
||||||
|
horizontal_messages,
|
||||||
validation_data: validation_data.clone(),
|
validation_data: validation_data.clone(),
|
||||||
relay_chain_state,
|
relay_chain_state,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
inherent_data
|
|
||||||
.put_data(VALIDATION_DATA_IDENTIFIER, &validation_data)
|
|
||||||
.map_err(|e| {
|
|
||||||
error!(
|
|
||||||
target: "cumulus-collator",
|
|
||||||
"Failed to put validation function params into inherent data: {:?}",
|
|
||||||
e,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.ok()?;
|
|
||||||
|
|
||||||
let message_ingestion_data = {
|
|
||||||
let downward_messages = self.retrieve_dmq_contents(relay_parent)?;
|
|
||||||
let horizontal_messages =
|
|
||||||
self.retrieve_all_inbound_hrmp_channel_contents(relay_parent)?;
|
|
||||||
|
|
||||||
inherents::MessageIngestionType {
|
|
||||||
downward_messages,
|
|
||||||
horizontal_messages,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inherent_data
|
inherent_data
|
||||||
.put_data(
|
.put_data(
|
||||||
inherents::MESSAGE_INGESTION_IDENTIFIER,
|
inherents::SYSTEM_INHERENT_IDENTIFIER,
|
||||||
&message_ingestion_data,
|
&system_inherent_data,
|
||||||
)
|
)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
error!(
|
error!(
|
||||||
target: "cumulus-collator",
|
target: "cumulus-collator",
|
||||||
"Failed to put downward messages into inherent data: {:?}",
|
"Failed to put the system inherent into inherent data: {:?}",
|
||||||
e,
|
e,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "cumulus-message-broker"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
# Substrate dependencies
|
|
||||||
frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
|
|
||||||
frame-system = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
|
|
||||||
sp-inherents = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
|
|
||||||
sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
|
|
||||||
|
|
||||||
# Other dependencies
|
|
||||||
codec = { package = "parity-scale-codec", version = "1.3.0", features = [ "derive" ], default-features = false }
|
|
||||||
|
|
||||||
# Cumulus dependencies
|
|
||||||
cumulus-primitives = { path = "../primitives", default-features = false }
|
|
||||||
cumulus-parachain-upgrade = { path = "../parachain-upgrade", default-features = false }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = [ "std" ]
|
|
||||||
std = [
|
|
||||||
"frame-support/std",
|
|
||||||
"frame-system/std",
|
|
||||||
"sp-inherents/std",
|
|
||||||
"cumulus-primitives/std",
|
|
||||||
"cumulus-parachain-upgrade/std",
|
|
||||||
"sp-std/std"
|
|
||||||
]
|
|
||||||
@@ -1,426 +0,0 @@
|
|||||||
// Copyright 2020 Parity Technologies (UK) Ltd.
|
|
||||||
// This file is part of Cumulus.
|
|
||||||
|
|
||||||
// Substrate is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// Substrate is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Cumulus. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//! This pallet deals with the low-level details of parachain message passing.
|
|
||||||
//!
|
|
||||||
//! Specifically, this pallet serves as a glue layer between cumulus collation pipeline and the
|
|
||||||
//! runtime that hosts this pallet.
|
|
||||||
|
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
|
||||||
|
|
||||||
use frame_support::{
|
|
||||||
decl_module, decl_storage, storage,
|
|
||||||
traits::Get,
|
|
||||||
weights::{DispatchClass, Weight},
|
|
||||||
StorageValue,
|
|
||||||
};
|
|
||||||
use frame_system::{ensure_none, ensure_root};
|
|
||||||
use sp_inherents::{InherentData, InherentIdentifier, MakeFatalError, ProvideInherent};
|
|
||||||
use sp_std::{cmp, prelude::*};
|
|
||||||
|
|
||||||
use cumulus_primitives::{
|
|
||||||
inherents::{MessageIngestionType, MESSAGE_INGESTION_IDENTIFIER},
|
|
||||||
well_known_keys, DownwardMessageHandler, HrmpMessageHandler, HrmpMessageSender,
|
|
||||||
OutboundHrmpMessage, ParaId, UpwardMessage, UpwardMessageSender,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Configuration trait of the message broker pallet.
|
|
||||||
pub trait Config: frame_system::Config + cumulus_parachain_upgrade::Config {
|
|
||||||
/// The downward message handlers that will be informed when a message is received.
|
|
||||||
type DownwardMessageHandlers: DownwardMessageHandler;
|
|
||||||
/// The HRMP message handlers that will be informed when a message is received.
|
|
||||||
type HrmpMessageHandlers: HrmpMessageHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
decl_storage! {
|
|
||||||
trait Store for Module<T: Config> as MessageBroker {
|
|
||||||
PendingUpwardMessages: Vec<UpwardMessage>;
|
|
||||||
|
|
||||||
/// Essentially `OutboundHrmpMessage`s grouped by the recipients.
|
|
||||||
OutboundHrmpMessages: map hasher(twox_64_concat) ParaId => Vec<Vec<u8>>;
|
|
||||||
/// HRMP channels with the given recipients are awaiting to be processed. If a `ParaId` is
|
|
||||||
/// present in this vector then `OutboundHrmpMessages` for it should be not empty.
|
|
||||||
NonEmptyHrmpChannels: Vec<ParaId>;
|
|
||||||
/// The number of HRMP messages we observed in `on_initialize` and thus used that number for
|
|
||||||
/// announcing the weight of `on_initialize` and `on_finialize`.
|
|
||||||
AnnouncedHrmpMessagesPerCandidate: u32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
decl_module! {
|
|
||||||
pub struct Module<T: Config> for enum Call where origin: T::Origin {
|
|
||||||
/// An entrypoint for an inherent to deposit downward messages into the runtime. It accepts
|
|
||||||
/// and processes the list of downward messages and inbound HRMP messages.
|
|
||||||
#[weight = (10, DispatchClass::Mandatory)]
|
|
||||||
fn ingest_inbound_messages(origin, messages: MessageIngestionType) {
|
|
||||||
ensure_none(origin)?;
|
|
||||||
|
|
||||||
let MessageIngestionType {
|
|
||||||
downward_messages,
|
|
||||||
horizontal_messages,
|
|
||||||
} = messages;
|
|
||||||
|
|
||||||
let dm_count = downward_messages.len() as u32;
|
|
||||||
for downward_message in downward_messages {
|
|
||||||
T::DownwardMessageHandlers::handle_downward_message(downward_message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store the processed_downward_messages here so that it's will be accessible from
|
|
||||||
// PVF's `validate_block` wrapper and collation pipeline.
|
|
||||||
storage::unhashed::put(
|
|
||||||
well_known_keys::PROCESSED_DOWNWARD_MESSAGES,
|
|
||||||
&dm_count,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut hrmp_watermark = None;
|
|
||||||
for (sender, channel_contents) in horizontal_messages {
|
|
||||||
for horizontal_message in channel_contents {
|
|
||||||
if hrmp_watermark
|
|
||||||
.map(|w| w < horizontal_message.sent_at)
|
|
||||||
.unwrap_or(true)
|
|
||||||
{
|
|
||||||
hrmp_watermark = Some(horizontal_message.sent_at);
|
|
||||||
}
|
|
||||||
|
|
||||||
T::HrmpMessageHandlers::handle_hrmp_message(sender, horizontal_message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we processed at least one message, then advance watermark to that location.
|
|
||||||
if let Some(hrmp_watermark) = hrmp_watermark {
|
|
||||||
storage::unhashed::put(
|
|
||||||
well_known_keys::HRMP_WATERMARK,
|
|
||||||
&hrmp_watermark,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[weight = (1_000, DispatchClass::Operational)]
|
|
||||||
fn sudo_send_upward_message(origin, message: UpwardMessage) {
|
|
||||||
ensure_root(origin)?;
|
|
||||||
let _ = Self::send_upward_message(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[weight = (1_000, DispatchClass::Operational)]
|
|
||||||
fn sudo_send_hrmp_message(origin, message: OutboundHrmpMessage) {
|
|
||||||
ensure_root(origin)?;
|
|
||||||
let _ = Self::send_hrmp_message(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_initialize() -> Weight {
|
|
||||||
let mut weight = T::DbWeight::get().writes(3);
|
|
||||||
storage::unhashed::kill(well_known_keys::HRMP_WATERMARK);
|
|
||||||
storage::unhashed::kill(well_known_keys::UPWARD_MESSAGES);
|
|
||||||
storage::unhashed::kill(well_known_keys::HRMP_OUTBOUND_MESSAGES);
|
|
||||||
|
|
||||||
// Here, in `on_initialize` we must report the weight for both `on_initialize` and
|
|
||||||
// `on_finalize`.
|
|
||||||
//
|
|
||||||
// One complication here, is that the `host_configuration` is updated by an inherent and
|
|
||||||
// those are processed after the block initialization phase. Therefore, we have to be
|
|
||||||
// content only with the configuration as per the previous block. That means that
|
|
||||||
// the configuration can be either stale (or be abscent altogether in case of the
|
|
||||||
// beginning of the chain).
|
|
||||||
//
|
|
||||||
// In order to mitigate this, we do the following. At the time, we are only concerned
|
|
||||||
// about `hrmp_max_message_num_per_candidate`. We reserve the amount of weight to process
|
|
||||||
// the number of HRMP messages according to the potentially stale configuration. In
|
|
||||||
// `on_finalize` we will process only the maximum between the announced number of messages
|
|
||||||
// and the actual received in the fresh configuration.
|
|
||||||
//
|
|
||||||
// In the common case, they will be the same. In the case the actual value is smaller
|
|
||||||
// than the announced, we would waste some of weight. In the case the actual value is
|
|
||||||
// greater than the announced, we will miss opportunity to send a couple of messages.
|
|
||||||
weight += T::DbWeight::get().reads_writes(1, 1);
|
|
||||||
let hrmp_max_message_num_per_candidate =
|
|
||||||
<cumulus_parachain_upgrade::Module<T>>::host_configuration()
|
|
||||||
.map(|cfg| cfg.hrmp_max_message_num_per_candidate)
|
|
||||||
.unwrap_or(0);
|
|
||||||
AnnouncedHrmpMessagesPerCandidate::put(hrmp_max_message_num_per_candidate);
|
|
||||||
|
|
||||||
// NOTE that the actual weight consumed by `on_finalize` may turn out lower.
|
|
||||||
weight += T::DbWeight::get().reads_writes(
|
|
||||||
3 + hrmp_max_message_num_per_candidate as u64,
|
|
||||||
4 + hrmp_max_message_num_per_candidate as u64,
|
|
||||||
);
|
|
||||||
|
|
||||||
weight
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_finalize() {
|
|
||||||
let host_config = <cumulus_parachain_upgrade::Module<T>>::host_configuration()
|
|
||||||
.expect("host configuration is promised to set until `on_finalize`; qed");
|
|
||||||
let relevant_messaging_state = <cumulus_parachain_upgrade::Module<T>>::relevant_messaging_state()
|
|
||||||
.expect("relevant messaging state is promised to be set until `on_finalize`; qed");
|
|
||||||
|
|
||||||
<Self as Store>::PendingUpwardMessages::mutate(|up| {
|
|
||||||
let (count, size) = relevant_messaging_state.relay_dispatch_queue_size;
|
|
||||||
|
|
||||||
let available_capacity = cmp::min(
|
|
||||||
host_config.max_upward_queue_count.saturating_sub(count),
|
|
||||||
host_config.max_upward_message_num_per_candidate,
|
|
||||||
);
|
|
||||||
let available_size = host_config.max_upward_queue_size.saturating_sub(size);
|
|
||||||
|
|
||||||
// Count the number of messages we can possibly fit in the given constraints, i.e.
|
|
||||||
// available_capacity and available_size.
|
|
||||||
let num = up
|
|
||||||
.iter()
|
|
||||||
.scan(
|
|
||||||
(available_capacity as usize, available_size as usize),
|
|
||||||
|(cnt, size), msg| match (cnt.checked_sub(1), size.checked_sub(msg.len())) {
|
|
||||||
(Some(cnt), Some(size)) => Some((cnt, size)),
|
|
||||||
_ => None,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.count();
|
|
||||||
|
|
||||||
// TODO: #274 Return back messages that do not longer fit into the queue.
|
|
||||||
|
|
||||||
storage::unhashed::put(well_known_keys::UPWARD_MESSAGES, &up[0..num]);
|
|
||||||
*up = up.split_off(num);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Sending HRMP messages is a little bit more involved. There are the following
|
|
||||||
// constraints:
|
|
||||||
//
|
|
||||||
// - a channel should exist (and it can be closed while a message is buffered),
|
|
||||||
// - at most one message can be sent in a channel,
|
|
||||||
// - the sent out messages should be ordered by ascension of recipient para id.
|
|
||||||
// - the capacity and total size of the channel is limited,
|
|
||||||
// - the maximum size of a message is limited (and can potentially be changed),
|
|
||||||
|
|
||||||
let mut non_empty_hrmp_channels = NonEmptyHrmpChannels::get();
|
|
||||||
// The number of messages we can send is limited by all of:
|
|
||||||
// - the number of non empty channels
|
|
||||||
// - the maximum number of messages per candidate according to the fresh config
|
|
||||||
// - the maximum number of messages per candidate according to the stale config
|
|
||||||
let outbound_hrmp_num =
|
|
||||||
non_empty_hrmp_channels.len()
|
|
||||||
.min(host_config.hrmp_max_message_num_per_candidate as usize)
|
|
||||||
.min(AnnouncedHrmpMessagesPerCandidate::get() as usize);
|
|
||||||
|
|
||||||
let mut outbound_hrmp_messages = Vec::with_capacity(outbound_hrmp_num);
|
|
||||||
let mut prune_empty = Vec::with_capacity(outbound_hrmp_num);
|
|
||||||
|
|
||||||
for &recipient in non_empty_hrmp_channels.iter() {
|
|
||||||
let idx = match relevant_messaging_state
|
|
||||||
.egress_channels
|
|
||||||
.binary_search_by_key(&recipient, |(recipient, _)| *recipient)
|
|
||||||
{
|
|
||||||
Ok(m) => m,
|
|
||||||
Err(_) => {
|
|
||||||
// TODO: #274 This means that there is no such channel anymore. Means that we should
|
|
||||||
// return back the messages from this channel.
|
|
||||||
//
|
|
||||||
// Until then pretend it became empty
|
|
||||||
prune_empty.push(recipient);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let channel_meta = &relevant_messaging_state.egress_channels[idx].1;
|
|
||||||
if channel_meta.msg_count + 1 > channel_meta.max_capacity {
|
|
||||||
// The channel is at its capacity. Skip it for now.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut pending = <Self as Store>::OutboundHrmpMessages::get(&recipient);
|
|
||||||
|
|
||||||
// This panics if `v` is empty. However, we are iterating only once over non-empty
|
|
||||||
// channels, therefore it cannot panic.
|
|
||||||
let message_payload = pending.remove(0);
|
|
||||||
let became_empty = pending.is_empty();
|
|
||||||
|
|
||||||
if channel_meta.total_size + message_payload.len() as u32 > channel_meta.max_total_size {
|
|
||||||
// Sending this message will make the channel total size overflow. Skip it for now.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we reached here, then the channel has capacity to receive this message. However,
|
|
||||||
// it doesn't mean that we are sending it just yet.
|
|
||||||
if became_empty {
|
|
||||||
OutboundHrmpMessages::remove(&recipient);
|
|
||||||
prune_empty.push(recipient);
|
|
||||||
} else {
|
|
||||||
OutboundHrmpMessages::insert(&recipient, pending);
|
|
||||||
}
|
|
||||||
|
|
||||||
if message_payload.len() as u32 > channel_meta.max_message_size {
|
|
||||||
// Apparently, the max message size was decreased since the message while the
|
|
||||||
// message was buffered. While it's possible to make another iteration to fetch
|
|
||||||
// the next message, we just keep going here to not complicate the logic too much.
|
|
||||||
//
|
|
||||||
// TODO: #274 Return back this message to sender.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
outbound_hrmp_messages.push(OutboundHrmpMessage {
|
|
||||||
recipient,
|
|
||||||
data: message_payload,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort the outbound messages by asceding recipient para id to satisfy the acceptance
|
|
||||||
// criteria requirement.
|
|
||||||
outbound_hrmp_messages.sort_by_key(|m| m.recipient);
|
|
||||||
|
|
||||||
// Prune hrmp channels that became empty. Additionally, because it may so happen that we
|
|
||||||
// only gave attention to some channels in `non_empty_hrmp_channels` it's important to
|
|
||||||
// change the order. Otherwise, the next `on_finalize` we will again give attention
|
|
||||||
// only to those channels that happen to be in the beginning, until they are emptied.
|
|
||||||
// This leads to "starvation" of the channels near to the end.
|
|
||||||
//
|
|
||||||
// To mitigate this we shift all processed elements towards the end of the vector using
|
|
||||||
// `rotate_left`. To get intution how it works see the examples in its rustdoc.
|
|
||||||
non_empty_hrmp_channels.retain(|x| !prune_empty.contains(x));
|
|
||||||
non_empty_hrmp_channels.rotate_left(outbound_hrmp_num - prune_empty.len());
|
|
||||||
|
|
||||||
<Self as Store>::NonEmptyHrmpChannels::put(non_empty_hrmp_channels);
|
|
||||||
storage::unhashed::put(
|
|
||||||
well_known_keys::HRMP_OUTBOUND_MESSAGES,
|
|
||||||
&outbound_hrmp_messages,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An error that can be raised upon sending an upward message.
|
|
||||||
pub enum SendUpErr {
|
|
||||||
/// The message sent is too big.
|
|
||||||
TooBig,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An error that can be raised upon sending a horizontal message.
|
|
||||||
pub enum SendHorizontalErr {
|
|
||||||
/// The message sent is too big.
|
|
||||||
TooBig,
|
|
||||||
/// There is no channel to the specified destination.
|
|
||||||
NoChannel,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Config> Module<T> {
|
|
||||||
pub fn send_upward_message(message: UpwardMessage) -> Result<(), SendUpErr> {
|
|
||||||
// Check if the message fits into the relay-chain constraints.
|
|
||||||
//
|
|
||||||
// Note, that we are using `host_configuration` here which may be from the previous
|
|
||||||
// block, in case this is called from `on_initialize`, i.e. before the inherent with fresh
|
|
||||||
// data is submitted.
|
|
||||||
//
|
|
||||||
// That shouldn't be a problem since this is a preliminary check and the actual check would
|
|
||||||
// be performed just before submitting the message from the candidate, and it already can
|
|
||||||
// happen that during the time the message is buffered for sending the relay-chain setting
|
|
||||||
// may change so that the message is no longer valid.
|
|
||||||
//
|
|
||||||
// However, changing this setting is expected to be rare.
|
|
||||||
match <cumulus_parachain_upgrade::Module<T>>::host_configuration() {
|
|
||||||
Some(cfg) => {
|
|
||||||
if message.len() > cfg.max_upward_message_size as usize {
|
|
||||||
return Err(SendUpErr::TooBig)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
// This storage field should carry over from the previous block. So if it's None
|
|
||||||
// then it must be that this is an edge-case where a message is attempted to be
|
|
||||||
// sent at the first block.
|
|
||||||
//
|
|
||||||
// Let's pass this message through. I think it's not unreasonable to expect that the
|
|
||||||
// message is not huge and it comes through, but if it doesn't it can be returned
|
|
||||||
// back to the sender.
|
|
||||||
//
|
|
||||||
// Thus fall through here.
|
|
||||||
}
|
|
||||||
};
|
|
||||||
<Self as Store>::PendingUpwardMessages::append(message);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn send_hrmp_message(message: OutboundHrmpMessage) -> Result<(), SendHorizontalErr> {
|
|
||||||
let OutboundHrmpMessage { recipient, data } = message;
|
|
||||||
|
|
||||||
// First, check if the message is addressed into an opened channel.
|
|
||||||
//
|
|
||||||
// Note, that we are using `relevant_messaging_state` which may be from the previous
|
|
||||||
// block, in case this is called from `on_initialize`, i.e. before the inherent with fresh
|
|
||||||
// data is submitted.
|
|
||||||
//
|
|
||||||
// That shouldn't be a problem though because this is anticipated and already can happen.
|
|
||||||
// This is because sending implies that a message is buffered until there is space to send
|
|
||||||
// a message in the candidate. After a while waiting in a buffer, it may be discovered that
|
|
||||||
// the channel to which a message were addressed is now closed. Another possibility, is that
|
|
||||||
// the maximum message size was decreased so that a message in the bufer doesn't fit. Should
|
|
||||||
// any of that happen the sender should be notified about the message was discarded.
|
|
||||||
//
|
|
||||||
// Here it a similar case, with the difference that the realization that the channel is closed
|
|
||||||
// came the same block.
|
|
||||||
let relevant_messaging_state = match <cumulus_parachain_upgrade::Module<T>>::relevant_messaging_state() {
|
|
||||||
Some(s) => s,
|
|
||||||
None => {
|
|
||||||
// This storage field should carry over from the previous block. So if it's None
|
|
||||||
// then it must be that this is an edge-case where a message is attempted to be
|
|
||||||
// sent at the first block. It should be safe to assume that there are no channels
|
|
||||||
// opened at all so early. At least, relying on this assumption seems to be a better
|
|
||||||
// tradeoff, compared to introducing an error variant that the clients should be
|
|
||||||
// prepared to handle.
|
|
||||||
return Err(SendHorizontalErr::NoChannel)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let channel_meta = match relevant_messaging_state
|
|
||||||
.egress_channels
|
|
||||||
.binary_search_by_key(&recipient, |(recipient, _)| *recipient)
|
|
||||||
{
|
|
||||||
Ok(idx) => &relevant_messaging_state.egress_channels[idx].1,
|
|
||||||
Err(_) => return Err(SendHorizontalErr::NoChannel),
|
|
||||||
};
|
|
||||||
if data.len() as u32 > channel_meta.max_message_size {
|
|
||||||
return Err(SendHorizontalErr::TooBig)
|
|
||||||
}
|
|
||||||
|
|
||||||
// And then at last update the storage.
|
|
||||||
<Self as Store>::OutboundHrmpMessages::append(&recipient, data);
|
|
||||||
<Self as Store>::NonEmptyHrmpChannels::mutate(|v| {
|
|
||||||
if !v.contains(&recipient) {
|
|
||||||
v.push(recipient);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Config> UpwardMessageSender for Module<T> {
|
|
||||||
fn send_upward_message(message: UpwardMessage) -> Result<(), ()> {
|
|
||||||
Self::send_upward_message(message).map_err(|_| ())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Config> HrmpMessageSender for Module<T> {
|
|
||||||
fn send_hrmp_message(message: OutboundHrmpMessage) -> Result<(), ()> {
|
|
||||||
Self::send_hrmp_message(message).map_err(|_| ())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Config> ProvideInherent for Module<T> {
|
|
||||||
type Call = Call<T>;
|
|
||||||
type Error = MakeFatalError<()>;
|
|
||||||
const INHERENT_IDENTIFIER: InherentIdentifier = MESSAGE_INGESTION_IDENTIFIER;
|
|
||||||
|
|
||||||
fn create_inherent(data: &InherentData) -> Option<Self::Call> {
|
|
||||||
data.get_data::<MessageIngestionType>(&MESSAGE_INGESTION_IDENTIFIER)
|
|
||||||
.expect("Downward messages inherent data failed to decode")
|
|
||||||
.map(|msgs| Call::ingest_inbound_messages(msgs))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cumulus-parachain-upgrade"
|
name = "cumulus-parachain-system"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,726 +0,0 @@
|
|||||||
// Copyright 2020 Parity Technologies (UK) Ltd.
|
|
||||||
// This file is part of Cumulus.
|
|
||||||
|
|
||||||
// Cumulus is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// Cumulus is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Cumulus. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
|
||||||
|
|
||||||
//! Enable Parachain validation function upgrades.
|
|
||||||
//!
|
|
||||||
//! Allow a user to determine when a parachain validation function upgrade
|
|
||||||
//! is legal, and perform the upgrade, triggering runtime events
|
|
||||||
//! for both storing and applying the new validation function.
|
|
||||||
//!
|
|
||||||
//! Depends on no external pallets or traits.
|
|
||||||
//!
|
|
||||||
//! This pallet depends on certain environmental conditions provided by
|
|
||||||
//! Cumulus. It will not work outside a Cumulus Parachain.
|
|
||||||
//!
|
|
||||||
//! Users must ensure that they register this pallet as an inherent provider.
|
|
||||||
|
|
||||||
use cumulus_primitives::{
|
|
||||||
inherents::{ValidationDataType, VALIDATION_DATA_IDENTIFIER as INHERENT_IDENTIFIER},
|
|
||||||
well_known_keys::{NEW_VALIDATION_CODE, VALIDATION_DATA}, AbridgedHostConfiguration,
|
|
||||||
OnValidationData, PersistedValidationData, ParaId, relay_chain,
|
|
||||||
};
|
|
||||||
use frame_support::{
|
|
||||||
decl_error, decl_event, decl_module, decl_storage, ensure, storage,
|
|
||||||
weights::{DispatchClass, Weight}, dispatch::DispatchResult, traits::Get,
|
|
||||||
};
|
|
||||||
use frame_system::{ensure_none, ensure_root};
|
|
||||||
use parachain::primitives::RelayChainBlockNumber;
|
|
||||||
use sp_core::storage::well_known_keys;
|
|
||||||
use sp_inherents::{InherentData, InherentIdentifier, ProvideInherent};
|
|
||||||
use sp_std::vec::Vec;
|
|
||||||
|
|
||||||
mod relay_state_snapshot;
|
|
||||||
|
|
||||||
pub use relay_state_snapshot::MessagingStateSnapshot;
|
|
||||||
|
|
||||||
/// The pallet's configuration trait.
|
|
||||||
pub trait Config: frame_system::Config {
|
|
||||||
/// The overarching event type.
|
|
||||||
type Event: From<Event> + Into<<Self as frame_system::Config>::Event>;
|
|
||||||
|
|
||||||
/// Something which can be notified when the validation data is set.
|
|
||||||
type OnValidationData: OnValidationData;
|
|
||||||
|
|
||||||
/// Returns the parachain ID we are running with.
|
|
||||||
type SelfParaId: Get<ParaId>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This pallet's storage items.
|
|
||||||
decl_storage! {
|
|
||||||
trait Store for Module<T: Config> as ParachainUpgrade {
|
|
||||||
// we need to store the new validation function for the span between
|
|
||||||
// setting it and applying it.
|
|
||||||
PendingValidationFunction get(fn new_validation_function):
|
|
||||||
Option<(RelayChainBlockNumber, Vec<u8>)>;
|
|
||||||
|
|
||||||
/// Were the [`ValidationData`] updated in this block?
|
|
||||||
DidUpdateValidationData: bool;
|
|
||||||
|
|
||||||
/// Were the validation data set to notify the relay chain?
|
|
||||||
DidSetValidationCode: bool;
|
|
||||||
|
|
||||||
/// The last relay parent block number at which we signalled the code upgrade.
|
|
||||||
LastUpgrade: relay_chain::BlockNumber;
|
|
||||||
|
|
||||||
/// The snapshot of some state related to messaging relevant to the current parachain as per
|
|
||||||
/// the relay parent.
|
|
||||||
///
|
|
||||||
/// This field is meant to be updated each block with the validation data inherent. Therefore,
|
|
||||||
/// before processing of the inherent, e.g. in `on_initialize` this data may be stale.
|
|
||||||
///
|
|
||||||
/// This data is also absent from the genesis.
|
|
||||||
RelevantMessagingState get(fn relevant_messaging_state): Option<MessagingStateSnapshot>;
|
|
||||||
/// The parachain host configuration that was obtained from the relay parent.
|
|
||||||
///
|
|
||||||
/// This field is meant to be updated each block with the validation data inherent. Therefore,
|
|
||||||
/// before processing of the inherent, e.g. in `on_initialize` this data may be stale.
|
|
||||||
///
|
|
||||||
/// This data is also absent from the genesis.
|
|
||||||
HostConfiguration get(fn host_configuration): Option<AbridgedHostConfiguration>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The pallet's dispatchable functions.
|
|
||||||
decl_module! {
|
|
||||||
pub struct Module<T: Config> for enum Call where origin: T::Origin {
|
|
||||||
// Initializing events
|
|
||||||
// this is needed only if you are using events in your pallet
|
|
||||||
fn deposit_event() = default;
|
|
||||||
|
|
||||||
// TODO: figure out a better weight than this
|
|
||||||
#[weight = (0, DispatchClass::Operational)]
|
|
||||||
pub fn schedule_upgrade(origin, validation_function: Vec<u8>) {
|
|
||||||
ensure_root(origin)?;
|
|
||||||
<frame_system::Module<T>>::can_set_code(&validation_function)?;
|
|
||||||
Self::schedule_upgrade_impl(validation_function)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Schedule a validation function upgrade without further checks.
|
|
||||||
///
|
|
||||||
/// Same as [`Module::schedule_upgrade`], but without checking that the new `validation_function`
|
|
||||||
/// is correct. This makes it more flexible, but also opens the door to easily brick the chain.
|
|
||||||
#[weight = (0, DispatchClass::Operational)]
|
|
||||||
pub fn schedule_upgrade_without_checks(origin, validation_function: Vec<u8>) {
|
|
||||||
ensure_root(origin)?;
|
|
||||||
Self::schedule_upgrade_impl(validation_function)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the current validation data.
|
|
||||||
///
|
|
||||||
/// This should be invoked exactly once per block. It will panic at the finalization
|
|
||||||
/// phase if the call was not invoked.
|
|
||||||
///
|
|
||||||
/// The dispatch origin for this call must be `Inherent`
|
|
||||||
///
|
|
||||||
/// As a side effect, this function upgrades the current validation function
|
|
||||||
/// if the appropriate time has come.
|
|
||||||
#[weight = (0, DispatchClass::Mandatory)]
|
|
||||||
fn set_validation_data(origin, data: ValidationDataType) -> DispatchResult {
|
|
||||||
ensure_none(origin)?;
|
|
||||||
assert!(!DidUpdateValidationData::exists(), "ValidationData must be updated only once in a block");
|
|
||||||
|
|
||||||
let ValidationDataType {
|
|
||||||
validation_data: vfp,
|
|
||||||
relay_chain_state,
|
|
||||||
} = data;
|
|
||||||
|
|
||||||
// initialization logic: we know that this runs exactly once every block,
|
|
||||||
// which means we can put the initialization logic here to remove the
|
|
||||||
// sequencing problem.
|
|
||||||
if let Some((apply_block, validation_function)) = PendingValidationFunction::get() {
|
|
||||||
if vfp.block_number >= apply_block {
|
|
||||||
PendingValidationFunction::kill();
|
|
||||||
LastUpgrade::put(&apply_block);
|
|
||||||
Self::put_parachain_code(&validation_function);
|
|
||||||
Self::deposit_event(Event::ValidationFunctionApplied(vfp.block_number));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let (host_config, relevant_messaging_state) =
|
|
||||||
relay_state_snapshot::extract_from_proof(
|
|
||||||
T::SelfParaId::get(),
|
|
||||||
vfp.relay_storage_root,
|
|
||||||
relay_chain_state
|
|
||||||
)
|
|
||||||
.map_err(|err| {
|
|
||||||
frame_support::debug::print!("invalid relay chain merkle proof: {:?}", err);
|
|
||||||
Error::<T>::InvalidRelayChainMerkleProof
|
|
||||||
})?;
|
|
||||||
|
|
||||||
storage::unhashed::put(VALIDATION_DATA, &vfp);
|
|
||||||
DidUpdateValidationData::put(true);
|
|
||||||
RelevantMessagingState::put(relevant_messaging_state);
|
|
||||||
HostConfiguration::put(host_config);
|
|
||||||
|
|
||||||
<T::OnValidationData as OnValidationData>::on_validation_data(vfp);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_finalize() {
|
|
||||||
assert!(DidUpdateValidationData::take(), "VFPs must be updated once per block");
|
|
||||||
DidSetValidationCode::take();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_initialize(n: T::BlockNumber) -> Weight {
|
|
||||||
// To prevent removing `NEW_VALIDATION_CODE` that was set by another `on_initialize` like
|
|
||||||
// for example from scheduler, we only kill the storage entry if it was not yet updated
|
|
||||||
// in the current block.
|
|
||||||
if !DidSetValidationCode::get() {
|
|
||||||
storage::unhashed::kill(NEW_VALIDATION_CODE);
|
|
||||||
}
|
|
||||||
|
|
||||||
storage::unhashed::kill(VALIDATION_DATA);
|
|
||||||
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Config> Module<T> {
|
|
||||||
/// Get validation data.
|
|
||||||
///
|
|
||||||
/// Returns `Some(_)` after the inherent set the data for the current block.
|
|
||||||
pub fn validation_data() -> Option<PersistedValidationData> {
|
|
||||||
storage::unhashed::get(VALIDATION_DATA)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Put a new validation function into a particular location where polkadot
|
|
||||||
/// monitors for updates. Calling this function notifies polkadot that a new
|
|
||||||
/// upgrade has been scheduled.
|
|
||||||
fn notify_polkadot_of_pending_upgrade(code: &[u8]) {
|
|
||||||
storage::unhashed::put_raw(NEW_VALIDATION_CODE, code);
|
|
||||||
DidSetValidationCode::put(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Put a new validation function into a particular location where this
|
|
||||||
/// parachain will execute it on subsequent blocks.
|
|
||||||
fn put_parachain_code(code: &[u8]) {
|
|
||||||
storage::unhashed::put_raw(well_known_keys::CODE, code);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The maximum code size permitted, in bytes.
|
|
||||||
///
|
|
||||||
/// Returns `None` if the relay chain parachain host configuration hasn't been submitted yet.
|
|
||||||
pub fn max_code_size() -> Option<u32> {
|
|
||||||
HostConfiguration::get().map(|cfg| cfg.max_code_size)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns if a PVF/runtime upgrade could be signalled at the current block, and if so
|
|
||||||
/// when the new code will take the effect.
|
|
||||||
fn code_upgrade_allowed(
|
|
||||||
vfp: &PersistedValidationData,
|
|
||||||
cfg: &AbridgedHostConfiguration,
|
|
||||||
) -> Option<relay_chain::BlockNumber> {
|
|
||||||
if PendingValidationFunction::get().is_some() {
|
|
||||||
// There is already upgrade scheduled. Upgrade is not allowed.
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let relay_blocks_since_last_upgrade = vfp
|
|
||||||
.block_number
|
|
||||||
.saturating_sub(LastUpgrade::get());
|
|
||||||
|
|
||||||
if relay_blocks_since_last_upgrade <= cfg.validation_upgrade_frequency {
|
|
||||||
// The cooldown after the last upgrade hasn't elapsed yet. Upgrade is not allowed.
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(vfp.block_number + cfg.validation_upgrade_delay)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The implementation of the runtime upgrade scheduling.
|
|
||||||
fn schedule_upgrade_impl(
|
|
||||||
validation_function: Vec<u8>,
|
|
||||||
) -> DispatchResult {
|
|
||||||
ensure!(
|
|
||||||
!PendingValidationFunction::exists(),
|
|
||||||
Error::<T>::OverlappingUpgrades
|
|
||||||
);
|
|
||||||
let vfp = Self::validation_data().ok_or(Error::<T>::ValidationDataNotAvailable)?;
|
|
||||||
let cfg = Self::host_configuration().ok_or(Error::<T>::HostConfigurationNotAvailable)?;
|
|
||||||
ensure!(
|
|
||||||
validation_function.len() <= cfg.max_code_size as usize,
|
|
||||||
Error::<T>::TooBig
|
|
||||||
);
|
|
||||||
let apply_block =
|
|
||||||
Self::code_upgrade_allowed(&vfp, &cfg).ok_or(Error::<T>::ProhibitedByPolkadot)?;
|
|
||||||
|
|
||||||
// When a code upgrade is scheduled, it has to be applied in two
|
|
||||||
// places, synchronized: both polkadot and the individual parachain
|
|
||||||
// have to upgrade on the same relay chain block.
|
|
||||||
//
|
|
||||||
// `notify_polkadot_of_pending_upgrade` notifies polkadot; the `PendingValidationFunction`
|
|
||||||
// storage keeps track locally for the parachain upgrade, which will
|
|
||||||
// be applied later.
|
|
||||||
Self::notify_polkadot_of_pending_upgrade(&validation_function);
|
|
||||||
PendingValidationFunction::put((apply_block, validation_function));
|
|
||||||
Self::deposit_event(Event::ValidationFunctionStored(apply_block));
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Config> ProvideInherent for Module<T> {
|
|
||||||
type Call = Call<T>;
|
|
||||||
type Error = sp_inherents::MakeFatalError<()>;
|
|
||||||
const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER;
|
|
||||||
|
|
||||||
fn create_inherent(data: &InherentData) -> Option<Self::Call> {
|
|
||||||
let data: ValidationDataType = data
|
|
||||||
.get_data(&INHERENT_IDENTIFIER)
|
|
||||||
.ok()
|
|
||||||
.flatten()
|
|
||||||
.expect("validation function params are always injected into inherent data; qed");
|
|
||||||
|
|
||||||
Some(Call::set_validation_data(data))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
decl_event! {
|
|
||||||
pub enum Event {
|
|
||||||
// The validation function has been scheduled to apply as of the contained relay chain block number.
|
|
||||||
ValidationFunctionStored(RelayChainBlockNumber),
|
|
||||||
// The validation function was applied as of the contained relay chain block number.
|
|
||||||
ValidationFunctionApplied(RelayChainBlockNumber),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
decl_error! {
|
|
||||||
pub enum Error for Module<T: Config> {
|
|
||||||
/// Attempt to upgrade validation function while existing upgrade pending
|
|
||||||
OverlappingUpgrades,
|
|
||||||
/// Polkadot currently prohibits this parachain from upgrading its validation function
|
|
||||||
ProhibitedByPolkadot,
|
|
||||||
/// The supplied validation function has compiled into a blob larger than Polkadot is willing to run
|
|
||||||
TooBig,
|
|
||||||
/// The inherent which supplies the validation data did not run this block
|
|
||||||
ValidationDataNotAvailable,
|
|
||||||
/// The inherent which supplies the host configuration did not run this block
|
|
||||||
HostConfigurationNotAvailable,
|
|
||||||
/// Invalid relay-chain storage merkle proof
|
|
||||||
InvalidRelayChainMerkleProof,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// tests for this pallet
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
use codec::Encode;
|
|
||||||
use cumulus_primitives::PersistedValidationData;
|
|
||||||
use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder;
|
|
||||||
use frame_support::{
|
|
||||||
assert_ok,
|
|
||||||
dispatch::UnfilteredDispatchable,
|
|
||||||
impl_outer_event, impl_outer_origin, parameter_types,
|
|
||||||
traits::{OnFinalize, OnInitialize},
|
|
||||||
};
|
|
||||||
use frame_system::{InitKind, RawOrigin};
|
|
||||||
use sp_core::H256;
|
|
||||||
use sp_runtime::{
|
|
||||||
testing::Header,
|
|
||||||
traits::{BlakeTwo256, IdentityLookup},
|
|
||||||
};
|
|
||||||
use sp_version::RuntimeVersion;
|
|
||||||
|
|
||||||
impl_outer_origin! {
|
|
||||||
pub enum Origin for Test where system = frame_system {}
|
|
||||||
}
|
|
||||||
|
|
||||||
mod parachain_upgrade {
|
|
||||||
pub use crate::Event;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_outer_event! {
|
|
||||||
pub enum TestEvent for Test {
|
|
||||||
frame_system<T>,
|
|
||||||
parachain_upgrade,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// For testing the pallet, we construct most of a mock runtime. This means
|
|
||||||
// first constructing a configuration type (`Test`) which `impl`s each of the
|
|
||||||
// configuration traits of modules we want to use.
|
|
||||||
#[derive(Clone, Eq, PartialEq)]
|
|
||||||
pub struct Test;
|
|
||||||
parameter_types! {
|
|
||||||
pub const BlockHashCount: u64 = 250;
|
|
||||||
pub Version: RuntimeVersion = RuntimeVersion {
|
|
||||||
spec_name: sp_version::create_runtime_str!("test"),
|
|
||||||
impl_name: sp_version::create_runtime_str!("system-test"),
|
|
||||||
authoring_version: 1,
|
|
||||||
spec_version: 1,
|
|
||||||
impl_version: 1,
|
|
||||||
apis: sp_version::create_apis_vec!([]),
|
|
||||||
transaction_version: 1,
|
|
||||||
};
|
|
||||||
pub const ParachainId: ParaId = ParaId::new(200);
|
|
||||||
}
|
|
||||||
impl frame_system::Config for Test {
|
|
||||||
type Origin = Origin;
|
|
||||||
type Call = ();
|
|
||||||
type Index = u64;
|
|
||||||
type BlockNumber = u64;
|
|
||||||
type Hash = H256;
|
|
||||||
type Hashing = BlakeTwo256;
|
|
||||||
type AccountId = u64;
|
|
||||||
type Lookup = IdentityLookup<Self::AccountId>;
|
|
||||||
type Header = Header;
|
|
||||||
type Event = TestEvent;
|
|
||||||
type BlockHashCount = BlockHashCount;
|
|
||||||
type BlockLength = ();
|
|
||||||
type BlockWeights = ();
|
|
||||||
type Version = Version;
|
|
||||||
type PalletInfo = ();
|
|
||||||
type AccountData = ();
|
|
||||||
type OnNewAccount = ();
|
|
||||||
type OnKilledAccount = ();
|
|
||||||
type DbWeight = ();
|
|
||||||
type BaseCallFilter = ();
|
|
||||||
type SystemWeightInfo = ();
|
|
||||||
type SS58Prefix = ();
|
|
||||||
}
|
|
||||||
impl Config for Test {
|
|
||||||
type Event = TestEvent;
|
|
||||||
type OnValidationData = ();
|
|
||||||
type SelfParaId = ParachainId;
|
|
||||||
}
|
|
||||||
|
|
||||||
type ParachainUpgrade = Module<Test>;
|
|
||||||
type System = frame_system::Module<Test>;
|
|
||||||
|
|
||||||
// This function basically just builds a genesis storage key/value store according to
|
|
||||||
// our desired mockup.
|
|
||||||
fn new_test_ext() -> sp_io::TestExternalities {
|
|
||||||
frame_system::GenesisConfig::default()
|
|
||||||
.build_storage::<Test>()
|
|
||||||
.unwrap()
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CallInWasm(Vec<u8>);
|
|
||||||
|
|
||||||
impl sp_core::traits::CallInWasm for CallInWasm {
|
|
||||||
fn call_in_wasm(
|
|
||||||
&self,
|
|
||||||
_wasm_code: &[u8],
|
|
||||||
_code_hash: Option<Vec<u8>>,
|
|
||||||
_method: &str,
|
|
||||||
_call_data: &[u8],
|
|
||||||
_ext: &mut dyn sp_externalities::Externalities,
|
|
||||||
_missing_host_functions: sp_core::traits::MissingHostFunctions,
|
|
||||||
) -> Result<Vec<u8>, String> {
|
|
||||||
Ok(self.0.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn wasm_ext() -> sp_io::TestExternalities {
|
|
||||||
let version = RuntimeVersion {
|
|
||||||
spec_name: "test".into(),
|
|
||||||
spec_version: 2,
|
|
||||||
impl_version: 1,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
let call_in_wasm = CallInWasm(version.encode());
|
|
||||||
|
|
||||||
let mut ext = new_test_ext();
|
|
||||||
ext.register_extension(sp_core::traits::CallInWasmExt::new(call_in_wasm));
|
|
||||||
ext
|
|
||||||
}
|
|
||||||
|
|
||||||
struct BlockTest {
|
|
||||||
n: <Test as frame_system::Config>::BlockNumber,
|
|
||||||
within_block: Box<dyn Fn()>,
|
|
||||||
after_block: Option<Box<dyn Fn()>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// BlockTests exist to test blocks with some setup: we have to assume that
|
|
||||||
/// `validate_block` will mutate and check storage in certain predictable
|
|
||||||
/// ways, for example, and we want to always ensure that tests are executed
|
|
||||||
/// in the context of some particular block number.
|
|
||||||
#[derive(Default)]
|
|
||||||
struct BlockTests {
|
|
||||||
tests: Vec<BlockTest>,
|
|
||||||
pending_upgrade: Option<RelayChainBlockNumber>,
|
|
||||||
ran: bool,
|
|
||||||
relay_sproof_builder_hook: Option<
|
|
||||||
Box<dyn Fn(&BlockTests, RelayChainBlockNumber, &mut RelayStateSproofBuilder)>
|
|
||||||
>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BlockTests {
|
|
||||||
fn new() -> BlockTests {
|
|
||||||
Default::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_raw(mut self, test: BlockTest) -> Self {
|
|
||||||
self.tests.push(test);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add<F>(self, n: <Test as frame_system::Config>::BlockNumber, within_block: F) -> Self
|
|
||||||
where
|
|
||||||
F: 'static + Fn(),
|
|
||||||
{
|
|
||||||
self.add_raw(BlockTest {
|
|
||||||
n,
|
|
||||||
within_block: Box::new(within_block),
|
|
||||||
after_block: None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_with_post_test<F1, F2>(
|
|
||||||
self,
|
|
||||||
n: <Test as frame_system::Config>::BlockNumber,
|
|
||||||
within_block: F1,
|
|
||||||
after_block: F2,
|
|
||||||
) -> Self
|
|
||||||
where
|
|
||||||
F1: 'static + Fn(),
|
|
||||||
F2: 'static + Fn(),
|
|
||||||
{
|
|
||||||
self.add_raw(BlockTest {
|
|
||||||
n,
|
|
||||||
within_block: Box::new(within_block),
|
|
||||||
after_block: Some(Box::new(after_block)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_relay_sproof_builder<F>(mut self, f: F) -> Self
|
|
||||||
where
|
|
||||||
F: 'static + Fn(&BlockTests, RelayChainBlockNumber, &mut RelayStateSproofBuilder)
|
|
||||||
{
|
|
||||||
self.relay_sproof_builder_hook = Some(Box::new(f));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(&mut self) {
|
|
||||||
self.ran = true;
|
|
||||||
wasm_ext().execute_with(|| {
|
|
||||||
for BlockTest {
|
|
||||||
n,
|
|
||||||
within_block,
|
|
||||||
after_block,
|
|
||||||
} in self.tests.iter()
|
|
||||||
{
|
|
||||||
// clear pending updates, as applicable
|
|
||||||
if let Some(upgrade_block) = self.pending_upgrade {
|
|
||||||
if n >= &upgrade_block.into() {
|
|
||||||
self.pending_upgrade = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// begin initialization
|
|
||||||
System::initialize(
|
|
||||||
&n,
|
|
||||||
&Default::default(),
|
|
||||||
&Default::default(),
|
|
||||||
InitKind::Full,
|
|
||||||
);
|
|
||||||
|
|
||||||
// now mess with the storage the way validate_block does
|
|
||||||
let mut sproof_builder = RelayStateSproofBuilder::default();
|
|
||||||
if let Some(ref hook) = self.relay_sproof_builder_hook {
|
|
||||||
hook(self, *n as RelayChainBlockNumber, &mut sproof_builder);
|
|
||||||
}
|
|
||||||
let (relay_storage_root, relay_chain_state) =
|
|
||||||
sproof_builder.into_state_root_and_proof();
|
|
||||||
let vfp = PersistedValidationData {
|
|
||||||
block_number: *n as RelayChainBlockNumber,
|
|
||||||
relay_storage_root,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
storage::unhashed::put(VALIDATION_DATA, &vfp);
|
|
||||||
storage::unhashed::kill(NEW_VALIDATION_CODE);
|
|
||||||
|
|
||||||
// It is insufficient to push the validation function params
|
|
||||||
// to storage; they must also be included in the inherent data.
|
|
||||||
let inherent_data = {
|
|
||||||
let mut inherent_data = InherentData::default();
|
|
||||||
inherent_data
|
|
||||||
.put_data(INHERENT_IDENTIFIER, &ValidationDataType {
|
|
||||||
validation_data: vfp.clone(),
|
|
||||||
relay_chain_state,
|
|
||||||
})
|
|
||||||
.expect("failed to put VFP inherent");
|
|
||||||
inherent_data
|
|
||||||
};
|
|
||||||
|
|
||||||
// execute the block
|
|
||||||
ParachainUpgrade::on_initialize(*n);
|
|
||||||
ParachainUpgrade::create_inherent(&inherent_data)
|
|
||||||
.expect("got an inherent")
|
|
||||||
.dispatch_bypass_filter(RawOrigin::None.into())
|
|
||||||
.expect("dispatch succeeded");
|
|
||||||
within_block();
|
|
||||||
ParachainUpgrade::on_finalize(*n);
|
|
||||||
|
|
||||||
// did block execution set new validation code?
|
|
||||||
if storage::unhashed::exists(NEW_VALIDATION_CODE) {
|
|
||||||
if self.pending_upgrade.is_some() {
|
|
||||||
panic!("attempted to set validation code while upgrade was pending");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// clean up
|
|
||||||
System::finalize();
|
|
||||||
if let Some(after_block) = after_block {
|
|
||||||
after_block();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for BlockTests {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if !self.ran {
|
|
||||||
self.run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic]
|
|
||||||
fn block_tests_run_on_drop() {
|
|
||||||
BlockTests::new().add(123, || {
|
|
||||||
panic!("if this test passes, block tests run properly")
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn requires_root() {
|
|
||||||
BlockTests::new().add(123, || {
|
|
||||||
assert_eq!(
|
|
||||||
ParachainUpgrade::schedule_upgrade(Origin::signed(1), Default::default()),
|
|
||||||
Err(sp_runtime::DispatchError::BadOrigin),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn requires_root_2() {
|
|
||||||
BlockTests::new().add(123, || {
|
|
||||||
assert_ok!(ParachainUpgrade::schedule_upgrade(
|
|
||||||
RawOrigin::Root.into(),
|
|
||||||
Default::default()
|
|
||||||
));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn events() {
|
|
||||||
BlockTests::new()
|
|
||||||
.with_relay_sproof_builder(|_, _, builder| {
|
|
||||||
builder.host_config.validation_upgrade_delay = 1000;
|
|
||||||
})
|
|
||||||
.add_with_post_test(
|
|
||||||
123,
|
|
||||||
|| {
|
|
||||||
assert_ok!(ParachainUpgrade::schedule_upgrade(
|
|
||||||
RawOrigin::Root.into(),
|
|
||||||
Default::default()
|
|
||||||
));
|
|
||||||
},
|
|
||||||
|| {
|
|
||||||
let events = System::events();
|
|
||||||
assert_eq!(
|
|
||||||
events[0].event,
|
|
||||||
TestEvent::parachain_upgrade(Event::ValidationFunctionStored(1123))
|
|
||||||
);
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.add_with_post_test(
|
|
||||||
1234,
|
|
||||||
|| {},
|
|
||||||
|| {
|
|
||||||
let events = System::events();
|
|
||||||
assert_eq!(
|
|
||||||
events[0].event,
|
|
||||||
TestEvent::parachain_upgrade(Event::ValidationFunctionApplied(1234))
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn non_overlapping() {
|
|
||||||
BlockTests::new()
|
|
||||||
.with_relay_sproof_builder(|_, _, builder| {
|
|
||||||
builder.host_config.validation_upgrade_delay = 1000;
|
|
||||||
})
|
|
||||||
.add(123, || {
|
|
||||||
assert_ok!(ParachainUpgrade::schedule_upgrade(
|
|
||||||
RawOrigin::Root.into(),
|
|
||||||
Default::default()
|
|
||||||
));
|
|
||||||
})
|
|
||||||
.add(234, || {
|
|
||||||
assert_eq!(
|
|
||||||
ParachainUpgrade::schedule_upgrade(RawOrigin::Root.into(), Default::default()),
|
|
||||||
Err(Error::<Test>::OverlappingUpgrades.into()),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn manipulates_storage() {
|
|
||||||
BlockTests::new()
|
|
||||||
.add(123, || {
|
|
||||||
assert!(
|
|
||||||
!PendingValidationFunction::exists(),
|
|
||||||
"validation function must not exist yet"
|
|
||||||
);
|
|
||||||
assert_ok!(ParachainUpgrade::schedule_upgrade(
|
|
||||||
RawOrigin::Root.into(),
|
|
||||||
Default::default()
|
|
||||||
));
|
|
||||||
assert!(
|
|
||||||
PendingValidationFunction::exists(),
|
|
||||||
"validation function must now exist"
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.add_with_post_test(
|
|
||||||
1234,
|
|
||||||
|| {},
|
|
||||||
|| {
|
|
||||||
assert!(
|
|
||||||
!PendingValidationFunction::exists(),
|
|
||||||
"validation function must have been unset"
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn checks_size() {
|
|
||||||
BlockTests::new()
|
|
||||||
.with_relay_sproof_builder(|_, _, builder| {
|
|
||||||
builder.host_config.max_code_size = 8;
|
|
||||||
})
|
|
||||||
.add(123, || {
|
|
||||||
assert_eq!(
|
|
||||||
ParachainUpgrade::schedule_upgrade(RawOrigin::Root.into(), vec![0; 64]),
|
|
||||||
Err(Error::<Test>::TooBig.into()),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+10
-17
@@ -46,25 +46,12 @@ pub mod inherents {
|
|||||||
use sp_inherents::InherentIdentifier;
|
use sp_inherents::InherentIdentifier;
|
||||||
use sp_std::{collections::btree_map::BTreeMap, vec::Vec};
|
use sp_std::{collections::btree_map::BTreeMap, vec::Vec};
|
||||||
|
|
||||||
/// Inherent identifier for message ingestion inherent.
|
/// The identifier for the parachain-system inherent.
|
||||||
pub const MESSAGE_INGESTION_IDENTIFIER: InherentIdentifier = *b"msgingst";
|
pub const SYSTEM_INHERENT_IDENTIFIER: InherentIdentifier = *b"sysi1337";
|
||||||
/// The data passed via a message ingestion inherent. Consists of a bundle of
|
|
||||||
/// DMP and HRMP messages.
|
|
||||||
#[derive(codec::Encode, codec::Decode, sp_core::RuntimeDebug, Clone, PartialEq)]
|
|
||||||
pub struct MessageIngestionType {
|
|
||||||
/// Downward messages in the order they were sent.
|
|
||||||
pub downward_messages: Vec<InboundDownwardMessage>,
|
|
||||||
/// HRMP messages grouped by channels. The messages in the inner vec must be in order they
|
|
||||||
/// were sent. In combination with the rule of no more than one message in a channel per block,
|
|
||||||
/// this means `sent_at` is **strictly** greater than the previous one (if any).
|
|
||||||
pub horizontal_messages: BTreeMap<ParaId, Vec<InboundHrmpMessage>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The identifier for the `set_validation_data` inherent.
|
/// The payload that system inherent carries.
|
||||||
pub const VALIDATION_DATA_IDENTIFIER: InherentIdentifier = *b"valfunp0";
|
|
||||||
/// The type of the inherent.
|
|
||||||
#[derive(codec::Encode, codec::Decode, sp_core::RuntimeDebug, Clone, PartialEq)]
|
#[derive(codec::Encode, codec::Decode, sp_core::RuntimeDebug, Clone, PartialEq)]
|
||||||
pub struct ValidationDataType {
|
pub struct SystemInherentData {
|
||||||
pub validation_data: crate::PersistedValidationData,
|
pub validation_data: crate::PersistedValidationData,
|
||||||
/// A storage proof of a predefined set of keys from the relay-chain.
|
/// A storage proof of a predefined set of keys from the relay-chain.
|
||||||
///
|
///
|
||||||
@@ -75,6 +62,12 @@ pub mod inherents {
|
|||||||
/// - the list of egress HRMP channels (in the list of recipients form)
|
/// - the list of egress HRMP channels (in the list of recipients form)
|
||||||
/// - the metadata for the egress HRMP channels
|
/// - the metadata for the egress HRMP channels
|
||||||
pub relay_chain_state: sp_trie::StorageProof,
|
pub relay_chain_state: sp_trie::StorageProof,
|
||||||
|
/// Downward messages in the order they were sent.
|
||||||
|
pub downward_messages: Vec<InboundDownwardMessage>,
|
||||||
|
/// HRMP messages grouped by channels. The messages in the inner vec must be in order they
|
||||||
|
/// were sent. In combination with the rule of no more than one message in a channel per block,
|
||||||
|
/// this means `sent_at` is **strictly** greater than the previous one (if any).
|
||||||
|
pub horizontal_messages: BTreeMap<ParaId, Vec<InboundHrmpMessage>>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ impl pallet_sudo::Config for Runtime {
|
|||||||
type Event = Event;
|
type Event = Event;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl cumulus_parachain_upgrade::Config for Runtime {
|
impl cumulus_parachain_system::Config for Runtime {
|
||||||
type Event = Event;
|
type Event = Event;
|
||||||
type OnValidationFunctionParams = ();
|
type OnValidationFunctionParams = ();
|
||||||
}
|
}
|
||||||
@@ -260,7 +260,7 @@ construct_runtime! {
|
|||||||
Contracts: cumulus_pallet_contracts::{Module, Call, Config, Storage, Event<T>},
|
Contracts: cumulus_pallet_contracts::{Module, Call, Config, Storage, Event<T>},
|
||||||
Sudo: pallet_sudo::{Module, Call, Storage, Config<T>, Event<T>},
|
Sudo: pallet_sudo::{Module, Call, Storage, Config<T>, Event<T>},
|
||||||
RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Module, Call, Storage},
|
RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Module, Call, Storage},
|
||||||
ParachainUpgrade: cumulus_parachain_upgrade::{Module, Call, Storage, Inherent, Event},
|
ParachainUpgrade: cumulus_parachain_system::{Module, Call, Storage, Inherent, Event},
|
||||||
MessageBroker: cumulus_message_broker::{Module, Call, Inherent, Event<T>},
|
MessageBroker: cumulus_message_broker::{Module, Call, Inherent, Event<T>},
|
||||||
TokenDealer: cumulus_token_dealer::{Module, Call, Event<T>},
|
TokenDealer: cumulus_token_dealer::{Module, Call, Event<T>},
|
||||||
TransactionPayment: pallet_transaction_payment::{Module, Storage},
|
TransactionPayment: pallet_transaction_payment::{Module, Storage},
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ impl<T: Config> Get<ParaId> for Module<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
decl_storage! {
|
decl_storage! {
|
||||||
trait Store for Module<T: Config> as ParachainUpgrade {
|
trait Store for Module<T: Config> as ParachainInfo {
|
||||||
ParachainId get(fn parachain_id) config(): ParaId = 100.into();
|
ParachainId get(fn parachain_id) config(): ParaId = 100.into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,9 +35,8 @@ pallet-transaction-payment = { git = "https://github.com/paritytech/substrate",
|
|||||||
|
|
||||||
# Cumulus dependencies
|
# Cumulus dependencies
|
||||||
cumulus-runtime = { path = "../../runtime", default-features = false }
|
cumulus-runtime = { path = "../../runtime", default-features = false }
|
||||||
cumulus-parachain-upgrade = { path = "../../parachain-upgrade", default-features = false }
|
cumulus-parachain-system = { path = "../../parachain-system", default-features = false }
|
||||||
cumulus-primitives = { path = "../../primitives", default-features = false }
|
cumulus-primitives = { path = "../../primitives", default-features = false }
|
||||||
cumulus-message-broker = { path = "../../message-broker", default-features = false }
|
|
||||||
xcm-handler = { path = "../../xcm-handler", default-features = false }
|
xcm-handler = { path = "../../xcm-handler", default-features = false }
|
||||||
|
|
||||||
# Polkadot dependencies
|
# Polkadot dependencies
|
||||||
@@ -76,8 +75,7 @@ std = [
|
|||||||
"parachain-info/std",
|
"parachain-info/std",
|
||||||
"rococo-parachain-primitives/std",
|
"rococo-parachain-primitives/std",
|
||||||
"cumulus-runtime/std",
|
"cumulus-runtime/std",
|
||||||
"cumulus-parachain-upgrade/std",
|
"cumulus-parachain-system/std",
|
||||||
"cumulus-message-broker/std",
|
|
||||||
"cumulus-primitives/std",
|
"cumulus-primitives/std",
|
||||||
"xcm/std",
|
"xcm/std",
|
||||||
"xcm-builder/std",
|
"xcm-builder/std",
|
||||||
|
|||||||
@@ -229,13 +229,10 @@ impl pallet_sudo::Config for Runtime {
|
|||||||
type Event = Event;
|
type Event = Event;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl cumulus_parachain_upgrade::Config for Runtime {
|
impl cumulus_parachain_system::Config for Runtime {
|
||||||
type Event = Event;
|
type Event = Event;
|
||||||
type OnValidationData = ();
|
type OnValidationData = ();
|
||||||
type SelfParaId = parachain_info::Module<Runtime>;
|
type SelfParaId = parachain_info::Module<Runtime>;
|
||||||
}
|
|
||||||
|
|
||||||
impl cumulus_message_broker::Config for Runtime {
|
|
||||||
type DownwardMessageHandlers = ();
|
type DownwardMessageHandlers = ();
|
||||||
type HrmpMessageHandlers = ();
|
type HrmpMessageHandlers = ();
|
||||||
}
|
}
|
||||||
@@ -290,8 +287,8 @@ impl Config for XcmConfig {
|
|||||||
impl xcm_handler::Config for Runtime {
|
impl xcm_handler::Config for Runtime {
|
||||||
type Event = Event;
|
type Event = Event;
|
||||||
type XcmExecutor = XcmExecutor<XcmConfig>;
|
type XcmExecutor = XcmExecutor<XcmConfig>;
|
||||||
type UpwardMessageSender = MessageBroker;
|
type UpwardMessageSender = ParachainSystem;
|
||||||
type HrmpMessageSender = MessageBroker;
|
type HrmpMessageSender = ParachainSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
construct_runtime! {
|
construct_runtime! {
|
||||||
@@ -305,8 +302,7 @@ construct_runtime! {
|
|||||||
Balances: pallet_balances::{Module, Call, Storage, Config<T>, Event<T>},
|
Balances: pallet_balances::{Module, Call, Storage, Config<T>, Event<T>},
|
||||||
Sudo: pallet_sudo::{Module, Call, Storage, Config<T>, Event<T>},
|
Sudo: pallet_sudo::{Module, Call, Storage, Config<T>, Event<T>},
|
||||||
RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Module, Call, Storage},
|
RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Module, Call, Storage},
|
||||||
ParachainUpgrade: cumulus_parachain_upgrade::{Module, Call, Storage, Inherent, Event},
|
ParachainSystem: cumulus_parachain_system::{Module, Call, Storage, Inherent, Event},
|
||||||
MessageBroker: cumulus_message_broker::{Module, Storage, Call, Inherent},
|
|
||||||
TransactionPayment: pallet_transaction_payment::{Module, Storage},
|
TransactionPayment: pallet_transaction_payment::{Module, Storage},
|
||||||
ParachainInfo: parachain_info::{Module, Storage, Config},
|
ParachainInfo: parachain_info::{Module, Storage, Config},
|
||||||
XcmHandler: xcm_handler::{Module, Event<T>, Origin},
|
XcmHandler: xcm_handler::{Module, Event<T>, Origin},
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
use crate::{Backend, Client};
|
use crate::{Backend, Client};
|
||||||
use cumulus_primitives::{
|
use cumulus_primitives::{
|
||||||
inherents::{ValidationDataType, VALIDATION_DATA_IDENTIFIER}, PersistedValidationData,
|
inherents::{SystemInherentData, SYSTEM_INHERENT_IDENTIFIER}, PersistedValidationData,
|
||||||
};
|
};
|
||||||
use cumulus_test_runtime::{Block, GetLastTimestamp};
|
use cumulus_test_runtime::{Block, GetLastTimestamp};
|
||||||
use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder;
|
use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder;
|
||||||
@@ -101,10 +101,12 @@ impl InitBlockBuilder for Client {
|
|||||||
|
|
||||||
inherent_data
|
inherent_data
|
||||||
.put_data(
|
.put_data(
|
||||||
VALIDATION_DATA_IDENTIFIER,
|
SYSTEM_INHERENT_IDENTIFIER,
|
||||||
&ValidationDataType {
|
&SystemInherentData {
|
||||||
validation_data,
|
validation_data,
|
||||||
relay_chain_state,
|
relay_chain_state,
|
||||||
|
downward_messages: Default::default(),
|
||||||
|
horizontal_messages: Default::default(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.expect("Put validation function params failed");
|
.expect("Put validation function params failed");
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ codec = { package = "parity-scale-codec", version = "1.0.5", default-features =
|
|||||||
# Substrate dependencies
|
# Substrate dependencies
|
||||||
sp-state-machine = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
|
sp-state-machine = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
|
||||||
sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
|
sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
|
||||||
|
sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
|
||||||
|
|
||||||
# Polkadot dependencies
|
# Polkadot dependencies
|
||||||
polkadot-primitives = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" }
|
polkadot-primitives = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" }
|
||||||
@@ -24,5 +25,6 @@ std = [
|
|||||||
"codec/std",
|
"codec/std",
|
||||||
"sp-state-machine/std",
|
"sp-state-machine/std",
|
||||||
"sp-runtime/std",
|
"sp-runtime/std",
|
||||||
|
"sp-std/std",
|
||||||
"cumulus-primitives/std",
|
"cumulus-primitives/std",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -14,19 +14,25 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Cumulus. If not, see <http://www.gnu.org/licenses/>.
|
// along with Cumulus. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use cumulus_primitives::{relay_chain, AbridgedHostConfiguration, AbridgedHrmpChannel, ParaId};
|
||||||
use sp_runtime::traits::HashFor;
|
use sp_runtime::traits::HashFor;
|
||||||
use sp_state_machine::MemoryDB;
|
use sp_state_machine::MemoryDB;
|
||||||
use cumulus_primitives::relay_chain;
|
use sp_std::collections::btree_map::BTreeMap;
|
||||||
|
|
||||||
/// Builds a sproof (portmanteau of 'spoof' and 'proof') of the relay chain state.
|
/// Builds a sproof (portmanteau of 'spoof' and 'proof') of the relay chain state.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RelayStateSproofBuilder {
|
pub struct RelayStateSproofBuilder {
|
||||||
pub host_config: cumulus_primitives::AbridgedHostConfiguration,
|
pub para_id: ParaId,
|
||||||
|
pub host_config: AbridgedHostConfiguration,
|
||||||
|
pub relay_dispatch_queue_size: Option<(u32, u32)>,
|
||||||
|
pub hrmp_egress_channel_index: Option<Vec<ParaId>>,
|
||||||
|
pub hrmp_channels: BTreeMap<relay_chain::v1::HrmpChannelId, AbridgedHrmpChannel>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for RelayStateSproofBuilder {
|
impl Default for RelayStateSproofBuilder {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
RelayStateSproofBuilder {
|
RelayStateSproofBuilder {
|
||||||
|
para_id: ParaId::from(200),
|
||||||
host_config: cumulus_primitives::AbridgedHostConfiguration {
|
host_config: cumulus_primitives::AbridgedHostConfiguration {
|
||||||
max_code_size: 2 * 1024 * 1024,
|
max_code_size: 2 * 1024 * 1024,
|
||||||
max_head_data_size: 1024 * 1024,
|
max_head_data_size: 1024 * 1024,
|
||||||
@@ -38,6 +44,9 @@ impl Default for RelayStateSproofBuilder {
|
|||||||
validation_upgrade_frequency: 6,
|
validation_upgrade_frequency: 6,
|
||||||
validation_upgrade_delay: 6,
|
validation_upgrade_delay: 6,
|
||||||
},
|
},
|
||||||
|
relay_dispatch_queue_size: None,
|
||||||
|
hrmp_egress_channel_index: None,
|
||||||
|
hrmp_channels: BTreeMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,6 +74,28 @@ impl RelayStateSproofBuilder {
|
|||||||
relay_chain::well_known_keys::ACTIVE_CONFIG.to_vec(),
|
relay_chain::well_known_keys::ACTIVE_CONFIG.to_vec(),
|
||||||
self.host_config.encode(),
|
self.host_config.encode(),
|
||||||
);
|
);
|
||||||
|
if let Some(relay_dispatch_queue_size) = self.relay_dispatch_queue_size {
|
||||||
|
insert(
|
||||||
|
relay_chain::well_known_keys::relay_dispatch_queue_size(self.para_id),
|
||||||
|
relay_dispatch_queue_size.encode(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Some(hrmp_egress_channel_index) = self.hrmp_egress_channel_index {
|
||||||
|
let mut sorted = hrmp_egress_channel_index.clone();
|
||||||
|
sorted.sort();
|
||||||
|
assert_eq!(sorted, hrmp_egress_channel_index,);
|
||||||
|
|
||||||
|
insert(
|
||||||
|
relay_chain::well_known_keys::hrmp_egress_channel_index(self.para_id),
|
||||||
|
hrmp_egress_channel_index.encode(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (channel, metadata) in self.hrmp_channels {
|
||||||
|
insert(
|
||||||
|
relay_chain::well_known_keys::hrmp_channels(channel),
|
||||||
|
metadata.encode(),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let root = backend.root().clone();
|
let root = backend.root().clone();
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ sp-transaction-pool = { git = "https://github.com/paritytech/substrate", default
|
|||||||
sp-version = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
|
sp-version = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
|
||||||
|
|
||||||
# Cumulus dependencies
|
# Cumulus dependencies
|
||||||
cumulus-parachain-upgrade = { path = "../../parachain-upgrade", default-features = false }
|
cumulus-parachain-system = { path = "../../parachain-system", default-features = false }
|
||||||
cumulus-primitives = { path = "../../primitives", default-features = false }
|
cumulus-primitives = { path = "../../primitives", default-features = false }
|
||||||
cumulus-runtime = { path = "../../runtime", default-features = false }
|
cumulus-runtime = { path = "../../runtime", default-features = false }
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ substrate-wasm-builder = "3.0.0"
|
|||||||
default = [ "std" ]
|
default = [ "std" ]
|
||||||
std = [
|
std = [
|
||||||
"codec/std",
|
"codec/std",
|
||||||
"cumulus-parachain-upgrade/std",
|
"cumulus-parachain-system/std",
|
||||||
"cumulus-primitives/std",
|
"cumulus-primitives/std",
|
||||||
"cumulus-runtime/std",
|
"cumulus-runtime/std",
|
||||||
"frame-executive/std",
|
"frame-executive/std",
|
||||||
|
|||||||
@@ -207,10 +207,12 @@ impl pallet_sudo::Config for Runtime {
|
|||||||
type Event = Event;
|
type Event = Event;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl cumulus_parachain_upgrade::Config for Runtime {
|
impl cumulus_parachain_system::Config for Runtime {
|
||||||
type SelfParaId = ParachainId;
|
type SelfParaId = ParachainId;
|
||||||
type Event = Event;
|
type Event = Event;
|
||||||
type OnValidationData = ();
|
type OnValidationData = ();
|
||||||
|
type DownwardMessageHandlers = ();
|
||||||
|
type HrmpMessageHandlers = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
parameter_types! {
|
parameter_types! {
|
||||||
@@ -228,7 +230,7 @@ construct_runtime! {
|
|||||||
Balances: pallet_balances::{Module, Call, Storage, Config<T>, Event<T>},
|
Balances: pallet_balances::{Module, Call, Storage, Config<T>, Event<T>},
|
||||||
Sudo: pallet_sudo::{Module, Call, Storage, Config<T>, Event<T>},
|
Sudo: pallet_sudo::{Module, Call, Storage, Config<T>, Event<T>},
|
||||||
RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Module, Call, Storage},
|
RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Module, Call, Storage},
|
||||||
ParachainUpgrade: cumulus_parachain_upgrade::{Module, Call, Storage, Inherent, Event},
|
ParachainSystem: cumulus_parachain_system::{Module, Call, Storage, Inherent, Event},
|
||||||
TransactionPayment: pallet_transaction_payment::{Module, Storage},
|
TransactionPayment: pallet_transaction_payment::{Module, Storage},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Cumulus. If not, see <http://www.gnu.org/licenses/>.
|
// along with Cumulus. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//! A pallet which implements the message-broker APIs for handling incoming XCM:
|
//! A pallet which implements the message handling APIs for handling incoming XCM:
|
||||||
//! * `DownwardMessageHandler`
|
//! * `DownwardMessageHandler`
|
||||||
//! * `HrmpMessageHandler`
|
//! * `HrmpMessageHandler`
|
||||||
//!
|
//!
|
||||||
|
|||||||
Reference in New Issue
Block a user