diff --git a/Cargo.lock b/Cargo.lock index f555a51c5d4b..e1b64fb9d3e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3922,6 +3922,7 @@ dependencies = [ "pallet-im-online", "pallet-indices", "pallet-membership", + "pallet-message-queue", "pallet-multisig", "pallet-nis", "pallet-nomination-pools", @@ -5963,6 +5964,25 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-message-queue" +version = "7.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#b7e0518966b145f22c16137da5c9796fcdb43b73" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-weights", +] + [[package]] name = "pallet-mmr" version = "4.0.0-dev" @@ -7884,6 +7904,7 @@ dependencies = [ "pallet-im-online", "pallet-indices", "pallet-membership", + "pallet-message-queue", "pallet-multisig", "pallet-nomination-pools", "pallet-nomination-pools-benchmarking", @@ -8045,6 +8066,7 @@ dependencies = [ "pallet-authorship", "pallet-babe", "pallet-balances", + "pallet-message-queue", "pallet-session", "pallet-staking", "pallet-timestamp", @@ -9276,6 +9298,7 @@ dependencies = [ "pallet-im-online", "pallet-indices", "pallet-membership", + "pallet-message-queue", "pallet-mmr", "pallet-multisig", "pallet-nis", @@ -14092,6 +14115,7 @@ dependencies = [ "pallet-im-online", "pallet-indices", "pallet-membership", + "pallet-message-queue", "pallet-multisig", "pallet-nomination-pools", "pallet-nomination-pools-benchmarking", @@ -14565,12 +14589,14 @@ dependencies = [ "parity-scale-codec", "polkadot-parachain", "polkadot-runtime-parachains", + "polkadot-test-runtime", "primitive-types", "scale-info", "sp-arithmetic", "sp-io", "sp-runtime", "sp-std", + "sp-weights", "xcm", "xcm-executor", ] @@ -14637,6 +14663,7 @@ dependencies = [ "sp-io", "sp-std", "xcm", + "xcm-builder", "xcm-executor", ] @@ -14648,6 +14675,7 @@ dependencies = [ "frame-system", "log", "pallet-balances", + "pallet-message-queue", "pallet-uniques", "pallet-xcm", "parity-scale-codec", @@ -14675,6 +14703,7 @@ dependencies = [ "frame-system", "honggfuzz", "pallet-balances", + "pallet-message-queue", "pallet-xcm", "parity-scale-codec", "polkadot-core-primitives", diff --git a/node/network/approval-distribution/src/lib.rs b/node/network/approval-distribution/src/lib.rs index c8ad21ad406e..82f62502e474 100644 --- a/node/network/approval-distribution/src/lib.rs +++ b/node/network/approval-distribution/src/lib.rs @@ -1270,11 +1270,11 @@ impl State { let candidate_entry = match block_entry.candidates.get(index as usize) { None => { gum::debug!( - target: LOG_TARGET, - ?hash, - ?index, - "`get_approval_signatures`: could not find candidate entry for given hash and index!" - ); + target: LOG_TARGET, + ?hash, + ?index, + "`get_approval_signatures`: could not find candidate entry for given hash and index!" + ); continue }, Some(e) => e, diff --git a/node/service/src/chain_spec.rs b/node/service/src/chain_spec.rs index a63f16dcb97b..d5c9d25698b6 100644 --- a/node/service/src/chain_spec.rs +++ b/node/service/src/chain_spec.rs @@ -17,7 +17,6 @@ //! Polkadot chain configurations. use beefy_primitives::crypto::AuthorityId as BeefyId; -use frame_support::weights::Weight; use grandpa::AuthorityId as GrandpaId; #[cfg(feature = "kusama-native")] use kusama_runtime as kusama; @@ -189,7 +188,6 @@ fn default_parachains_host_configuration( max_upward_queue_count: 8, max_upward_queue_size: 1024 * 1024, max_downward_message_size: 1024 * 1024, - ump_service_total_weight: Weight::from_parts(100_000_000_000, MAX_POV_SIZE as u64), max_upward_message_size: 50 * 1024, max_upward_message_num_per_candidate: 5, hrmp_sender_deposit: 0, diff --git a/primitives/src/v4/mod.rs b/primitives/src/v4/mod.rs index 56fe856d1504..4a40a5993c19 100644 --- a/primitives/src/v4/mod.rs +++ b/primitives/src/v4/mod.rs @@ -133,7 +133,7 @@ pub type ValidatorSignature = validator_app::Signature; /// A declarations of storage keys where an external observer can find some interesting data. pub mod well_known_keys { - use super::{HrmpChannelId, Id}; + use super::{HrmpChannelId, Id, WellKnownKey}; use hex_literal::hex; use parity_scale_codec::Encode as _; use sp_io::hashing::twox_64; @@ -210,6 +210,7 @@ pub mod well_known_keys { /// /// - `count: u32`, the number of messages currently in the queue for given para, /// - `total_size: u32`, the total size of all messages in the queue. + #[deprecated = "Use `relay_dispatch_queue_remaining_capacity` instead"] pub fn relay_dispatch_queue_size(para_id: Id) -> Vec { let prefix = hex!["f5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e"]; @@ -224,6 +225,24 @@ pub mod well_known_keys { }) } + /// Type safe version of `relay_dispatch_queue_size`. + #[deprecated = "Use `relay_dispatch_queue_remaining_capacity` instead"] + pub fn relay_dispatch_queue_size_typed(para: Id) -> WellKnownKey<(u32, u32)> { + #[allow(deprecated)] + relay_dispatch_queue_size(para).into() + } + + /// The upward message dispatch queue remaining capacity for the given para id. + /// + /// The storage entry stores a tuple of two values: + /// + /// - `count: u32`, the number of additional messages which may be enqueued for the given para, + /// - `total_size: u32`, the total size of additional messages which may be enqueued for the + /// given para. + pub fn relay_dispatch_queue_remaining_capacity(para_id: Id) -> WellKnownKey<(u32, u32)> { + (b":relay_dispatch_queue_remaining_capacity", para_id).encode().into() + } + /// The HRMP channel for the given identifier. /// /// The storage entry should be accessed as an `AbridgedHrmpChannel` encoded value. @@ -1706,6 +1725,42 @@ impl PvfCheckStatement { } } +/// A well-known and typed storage key. +/// +/// Allows for type-safe access to raw well-known storage keys. +pub struct WellKnownKey { + /// The raw storage key. + pub key: Vec, + _p: sp_std::marker::PhantomData, +} + +impl From> for WellKnownKey { + fn from(key: Vec) -> Self { + Self { key, _p: Default::default() } + } +} + +impl AsRef<[u8]> for WellKnownKey { + fn as_ref(&self) -> &[u8] { + self.key.as_ref() + } +} + +impl WellKnownKey { + /// Gets the value or `None` if it does not exist or decoding failed. + pub fn get(&self) -> Option { + sp_io::storage::get(&self.key) + .and_then(|raw| parity_scale_codec::DecodeAll::decode_all(&mut raw.as_ref()).ok()) + } +} + +impl WellKnownKey { + /// Sets the value. + pub fn set(&self, value: T) { + sp_io::storage::set(&self.key, &value.encode()); + } +} + /// Type discriminator for PVF preparation timeouts #[derive(Encode, Decode, TypeInfo, Clone, Copy, Debug, PartialEq, Eq)] #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] diff --git a/roadmap/implementers-guide/src/runtime/inclusion.md b/roadmap/implementers-guide/src/runtime/inclusion.md index 6df34ae4ddc1..db37ae10a198 100644 --- a/roadmap/implementers-guide/src/runtime/inclusion.md +++ b/roadmap/implementers-guide/src/runtime/inclusion.md @@ -1,6 +1,6 @@ # Inclusion Module -The inclusion module is responsible for inclusion and availability of scheduled parachains and parathreads. +The inclusion module is responsible for inclusion and availability of scheduled parachains and parathreads. It also manages the UMP dispatch queue of each parachain/thread. ## Storage @@ -35,11 +35,27 @@ PendingAvailability: map ParaId => CandidatePendingAvailability; PendingAvailabilityCommitments: map ParaId => CandidateCommitments; ``` +## Config Dependencies + +* `MessageQueue`: + The message queue provides general queueing and processing functionality. Currently it + replaces the old `UMP` dispatch queue. Other use-cases can be implemented as well by + adding new variants to `AggregateMessageOrigin`. Normally it should be set to an instance + of the `MessageQueue` pallet. + ## Session Change 1. Clear out all candidates pending availability. 1. Clear out all validator bitfields. +Optional: +1. The UMP queue of all outgoing paras can be "swept". This would prevent the dispatch queue from automatically being serviced. It is a consideration for the chain and specific behaviour is not defined. + +## Initialization + +No initialization routine runs for this module. However, the initialization of the `MessageQueue` pallet will attempt to process any pending UMP messages. + + ## Routines All failed checks should lead to an unrecoverable error making the block invalid. @@ -88,7 +104,7 @@ All failed checks should lead to an unrecoverable error making the block invalid 1. Ensure that any code upgrade scheduled by the candidate does not happen within `config.validation_upgrade_cooldown` of `Paras::last_code_upgrade(para_id, true)`, if any, comparing against the value of `Paras::FutureCodeUpgrades` for the given para ID. 1. Check the collator's signature on the candidate data. 1. check the backing of the candidate using the signatures and the bitfields, comparing against the validators assigned to the groups, fetched with the `group_validators` lookup. - 1. call `Ump::check_upward_messages(para, commitments.upward_messages)` to check that the upward messages are valid. + 1. call `check_upward_messages(config, para, commitments.upward_messages)` to check that the upward messages are valid. 1. call `Dmp::check_processed_downward_messages(para, commitments.processed_downward_messages)` to check that the DMQ is properly drained. 1. call `Hrmp::check_hrmp_watermark(para, commitments.hrmp_watermark)` for each candidate to check rules of processing the HRMP watermark. 1. using `Hrmp::check_outbound_hrmp(sender, commitments.horizontal_messages)` ensure that the each candidate sent a valid set of horizontal messages @@ -99,7 +115,7 @@ All failed checks should lead to an unrecoverable error making the block invalid 1. If the receipt contains a code upgrade, Call `Paras::schedule_code_upgrade(para_id, code, relay_parent_number, config)`. > TODO: Note that this is safe as long as we never enact candidates where the relay parent is across a session boundary. In that case, which we should be careful to avoid with contextual execution, the configuration might have changed and the para may de-sync from the host's understanding of it. 1. Reward all backing validators of each candidate, contained within the `backers` field. - 1. call `Ump::receive_upward_messages` for each backed candidate, using the [`UpwardMessage`s](../types/messages.md#upward-message) from the [`CandidateCommitments`](../types/candidate.md#candidate-commitments). + 1. call `receive_upward_messages` for each backed candidate, using the [`UpwardMessage`s](../types/messages.md#upward-message) from the [`CandidateCommitments`](../types/candidate.md#candidate-commitments). 1. call `Dmp::prune_dmq` with the para id of the candidate and the candidate's `processed_downward_messages`. 1. call `Hrmp::prune_hrmp` with the para id of the candiate and the candidate's `hrmp_watermark`. 1. call `Hrmp::queue_outbound_hrmp` with the para id of the candidate and the list of horizontal messages taken from the commitment, @@ -118,3 +134,20 @@ All failed checks should lead to an unrecoverable error making the block invalid * `candidate_pending_availability(ParaId) -> Option`: returns the `CommittedCandidateReceipt` pending availability for the para provided, if any. * `pending_availability(ParaId) -> Option`: returns the metadata around the candidate pending availability for the para, if any. * `collect_disputed(disputed: Vec) -> Vec`: Sweeps through all paras pending availability. If the candidate hash is one of the disputed candidates, then clean up the corresponding storage for that candidate and the commitments. Return a vector of cleaned-up core IDs. + +These functions were formerly part of the UMP pallet: + +* `check_upward_messages(P: ParaId, Vec)`: + 1. Checks that the parachain is not currently offboarding and error otherwise. + 1. Checks that there are at most `config.max_upward_message_num_per_candidate` messages to be enqueued. + 1. Checks that no message exceeds `config.max_upward_message_size`. + 1. Checks that the total resulting queue size would not exceed `co`. + 1. Verify that queuing up the messages could not result in exceeding the queue's footprint + according to the config items `config.max_upward_queue_count` and `config.max_upward_queue_size`. The queue's current footprint is provided in `well_known_keys` + in order to facilitate oraclisation on to the para. + +Candidate Enactment: + +* `receive_upward_messages(P: ParaId, Vec)`: + 1. Process each upward message `M` in order: + 1. Place in the dispatch queue according to its para ID (or handle it immediately). diff --git a/roadmap/implementers-guide/src/runtime/ump.md b/roadmap/implementers-guide/src/runtime/ump.md deleted file mode 100644 index 79e06e5ed0af..000000000000 --- a/roadmap/implementers-guide/src/runtime/ump.md +++ /dev/null @@ -1,87 +0,0 @@ -# UMP Module - -A module responsible for Upward Message Passing (UMP). See [Messaging Overview](../messaging.md) for more details. - -## Storage - -Storage related to UMP - -```rust -/// The messages waiting to be handled by the relay-chain originating from a certain parachain. -/// -/// Note that some upward messages might have been already processed by the inclusion logic. E.g. -/// channel management messages. -/// -/// The messages are processed in FIFO order. -RelayDispatchQueues: map ParaId => Vec; -/// Size of the dispatch queues. Caches sizes of the queues in `RelayDispatchQueue`. -/// -/// First item in the tuple is the count of messages and second -/// is the total length (in bytes) of the message payloads. -/// -/// Note that this is an auxilary mapping: it's possible to tell the byte size and the number of -/// messages only looking at `RelayDispatchQueues`. This mapping is separate to avoid the cost of -/// loading the whole message queue if only the total size and count are required. -/// -/// Invariant: -/// - The set of keys should exactly match the set of keys of `RelayDispatchQueues`. -RelayDispatchQueueSize: map ParaId => (u32, u32); // (num_messages, total_bytes) -/// The ordered list of `ParaId`s that have a `RelayDispatchQueue` entry. -/// -/// Invariant: -/// - The set of items from this vector should be exactly the set of the keys in -/// `RelayDispatchQueues` and `RelayDispatchQueueSize`. -NeedsDispatch: Vec; -/// This is the para that gets dispatched first during the next upward dispatchable queue -/// execution round. -/// -/// Invariant: -/// - If `Some(para)`, then `para` must be present in `NeedsDispatch`. -NextDispatchRoundStartWith: Option; -``` - - -## Initialization - -No initialization routine runs for this module. - -## Routines - -Candidate Acceptance Function: - -* `check_upward_messages(P: ParaId, Vec`): - 1. Checks that there are at most `config.max_upward_message_num_per_candidate` messages. - 1. Checks that no message exceeds `config.max_upward_message_size`. - 1. Verify that `RelayDispatchQueueSize` for `P` has enough capacity for the messages - -Candidate Enactment: - -* `receive_upward_messages(P: ParaId, Vec)`: - 1. Process each upward message `M` in order: - 1. Append the message to `RelayDispatchQueues` for `P` - 1. Increment the size and the count in `RelayDispatchQueueSize` for `P`. - 1. Ensure that `P` is present in `NeedsDispatch`. - -The following routine is meant to execute pending entries in upward message queues. This function doesn't fail, even if -dispatching any of individual upward messages returns an error. - -`process_pending_upward_messages()`: - 1. Initialize a cumulative weight counter `T` to 0 - 1. Iterate over items in `NeedsDispatch` cyclically, starting with `NextDispatchRoundStartWith`. If the item specified is `None` start from the beginning. For each `P` encountered: - 1. Dequeue the first upward message `D` from `RelayDispatchQueues` for `P` - 1. Decrement the size of the message from `RelayDispatchQueueSize` for `P` - 1. Delegate processing of the message to the runtime. The weight consumed is added to `T`. - 1. If `T >= config.ump_service_total_weight`, set `NextDispatchRoundStartWith` to `P` and finish processing. - 1. If `RelayDispatchQueues` for `P` became empty, remove `P` from `NeedsDispatch`. - 1. If `NeedsDispatch` became empty then finish processing and set `NextDispatchRoundStartWith` to `None`. - > NOTE that in practice we would need to approach the weight calculation more thoroughly, i.e. incorporate all operations - > that could take place on the course of handling these upward messages. - -## Session Change - -1. For each `P` in `outgoing_paras` (generated by `Paras::on_new_session`): - 1. Remove `RelayDispatchQueueSize` of `P`. - 1. Remove `RelayDispatchQueues` of `P`. - 1. Remove `P` if it exists in `NeedsDispatch`. - 1. If `P` is in `NextDispatchRoundStartWith`, then reset it to `None` - - Note that if we don't remove the open/close requests since they are going to die out naturally at the end of the session. diff --git a/roadmap/implementers-guide/src/types/runtime.md b/roadmap/implementers-guide/src/types/runtime.md index e3fc3f0a6527..55c0a571b6c8 100644 --- a/roadmap/implementers-guide/src/types/runtime.md +++ b/roadmap/implementers-guide/src/types/runtime.md @@ -63,11 +63,6 @@ struct HostConfiguration { /// no further messages may be added to it. If it exceeds this then the queue may contain only /// a single message. pub max_upward_queue_size: u32, - /// The amount of weight we wish to devote to the processing the dispatchable upward messages - /// stage. - /// - /// NOTE that this is a soft limit and could be exceeded. - pub ump_service_total_weight: Weight, /// The maximum size of an upward message that can be sent by a candidate. /// /// This parameter affects the upper bound of size of `CandidateCommitments`. diff --git a/runtime/common/src/assigned_slots.rs b/runtime/common/src/assigned_slots.rs index 4438ead9ff66..b2c43b0d5b3f 100644 --- a/runtime/common/src/assigned_slots.rs +++ b/runtime/common/src/assigned_slots.rs @@ -649,6 +649,7 @@ mod tests { type RuntimeEvent = RuntimeEvent; type WeightInfo = parachains_paras::TestWeightInfo; type UnsignedPriority = ParasUnsignedPriority; + type QueueFootprinter = (); type NextSessionRotation = crate::mock::TestNextSessionRotation; } diff --git a/runtime/common/src/integration_tests.rs b/runtime/common/src/integration_tests.rs index b4e122eb2ef6..30e4758d24c1 100644 --- a/runtime/common/src/integration_tests.rs +++ b/runtime/common/src/integration_tests.rs @@ -205,6 +205,7 @@ impl paras::Config for Test { type RuntimeEvent = RuntimeEvent; type WeightInfo = paras::TestWeightInfo; type UnsignedPriority = ParasUnsignedPriority; + type QueueFootprinter = (); type NextSessionRotation = crate::mock::TestNextSessionRotation; } diff --git a/runtime/common/src/paras_registrar.rs b/runtime/common/src/paras_registrar.rs index 6394a413f385..0dbc47ee2ae8 100644 --- a/runtime/common/src/paras_registrar.rs +++ b/runtime/common/src/paras_registrar.rs @@ -773,6 +773,7 @@ mod tests { type RuntimeEvent = RuntimeEvent; type WeightInfo = paras::TestWeightInfo; type UnsignedPriority = ParasUnsignedPriority; + type QueueFootprinter = (); type NextSessionRotation = crate::mock::TestNextSessionRotation; } diff --git a/runtime/common/src/paras_sudo_wrapper.rs b/runtime/common/src/paras_sudo_wrapper.rs index 75f91919a9c2..8944e932e9ef 100644 --- a/runtime/common/src/paras_sudo_wrapper.rs +++ b/runtime/common/src/paras_sudo_wrapper.rs @@ -24,7 +24,7 @@ use primitives::Id as ParaId; use runtime_parachains::{ configuration, dmp, hrmp, paras::{self, ParaGenesisArgs}, - ump, ParaLifecycle, + ParaLifecycle, }; use sp_std::boxed::Box; @@ -37,10 +37,7 @@ pub mod pallet { #[pallet::config] #[pallet::disable_frame_system_supertrait_check] - pub trait Config: - configuration::Config + paras::Config + dmp::Config + ump::Config + hrmp::Config - { - } + pub trait Config: configuration::Config + paras::Config + dmp::Config + hrmp::Config {} #[pallet::error] pub enum Error { diff --git a/runtime/kusama/Cargo.toml b/runtime/kusama/Cargo.toml index 4e082eb5acd7..acb58ee96513 100644 --- a/runtime/kusama/Cargo.toml +++ b/runtime/kusama/Cargo.toml @@ -59,6 +59,7 @@ pallet-identity = { git = "https://github.com/paritytech/substrate", branch = "m pallet-im-online = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-indices = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-membership = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-message-queue = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-multisig = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-nomination-pools = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-offences = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -158,6 +159,7 @@ std = [ "pallet-im-online/std", "pallet-indices/std", "pallet-membership/std", + "pallet-message-queue/std", "pallet-multisig/std", "pallet-nomination-pools/std", "pallet-nomination-pools-runtime-api/std", @@ -225,6 +227,7 @@ runtime-benchmarks = [ "pallet-im-online/runtime-benchmarks", "pallet-indices/runtime-benchmarks", "pallet-membership/runtime-benchmarks", + "pallet-message-queue/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", "pallet-nomination-pools/runtime-benchmarks", "pallet-nomination-pools-benchmarking/runtime-benchmarks", @@ -276,6 +279,7 @@ try-runtime = [ "pallet-im-online/try-runtime", "pallet-indices/try-runtime", "pallet-membership/try-runtime", + "pallet-message-queue/try-runtime", "pallet-multisig/try-runtime", "pallet-nomination-pools/try-runtime", "pallet-offences/try-runtime", diff --git a/runtime/kusama/src/lib.rs b/runtime/kusama/src/lib.rs index b0136170a933..6971e90e974f 100644 --- a/runtime/kusama/src/lib.rs +++ b/runtime/kusama/src/lib.rs @@ -39,12 +39,14 @@ use sp_std::{cmp::Ordering, collections::btree_map::BTreeMap, prelude::*}; use runtime_parachains::{ configuration as parachains_configuration, disputes as parachains_disputes, - disputes::slashing as parachains_slashing, dmp as parachains_dmp, hrmp as parachains_hrmp, - inclusion as parachains_inclusion, initializer as parachains_initializer, - origin as parachains_origin, paras as parachains_paras, + disputes::slashing as parachains_slashing, + dmp as parachains_dmp, hrmp as parachains_hrmp, inclusion as parachains_inclusion, + inclusion::{AggregateMessageOrigin, UmpQueueId}, + initializer as parachains_initializer, origin as parachains_origin, paras as parachains_paras, paras_inherent as parachains_paras_inherent, reward_points as parachains_reward_points, - runtime_api_impl::v4 as parachains_runtime_api_impl, scheduler as parachains_scheduler, - session_info as parachains_session_info, shared as parachains_shared, ump as parachains_ump, + runtime_api_impl::v4 as parachains_runtime_api_impl, + scheduler as parachains_scheduler, session_info as parachains_session_info, + shared as parachains_shared, }; use authority_discovery_primitives::AuthorityId as AuthorityDiscoveryId; @@ -56,9 +58,9 @@ use frame_support::{ construct_runtime, parameter_types, traits::{ ConstU32, Contains, EitherOf, EitherOfDiverse, InstanceFilter, KeyOwnerProofSystem, - PrivilegeCmp, StorageMapShim, WithdrawReasons, + PrivilegeCmp, ProcessMessage, ProcessMessageError, StorageMapShim, WithdrawReasons, }, - weights::ConstantMultiplier, + weights::{ConstantMultiplier, WeightMeter}, PalletId, RuntimeDebug, }; use frame_system::EnsureRoot; @@ -81,6 +83,7 @@ use sp_staking::SessionIndex; #[cfg(any(feature = "std", test))] use sp_version::NativeVersion; use sp_version::RuntimeVersion; +use xcm::latest::Junction; pub use frame_system::Call as SystemCall; pub use pallet_balances::Call as BalancesCall; @@ -1088,6 +1091,8 @@ impl parachains_inclusion::Config for Runtime { type RuntimeEvent = RuntimeEvent; type DisputesHandler = ParasDisputes; type RewardValidators = parachains_reward_points::RewardValidatorsWithEraPoints; + type MessageQueue = MessageQueue; + type WeightInfo = weights::runtime_parachains_inclusion::WeightInfo; } parameter_types! { @@ -1098,20 +1103,55 @@ impl parachains_paras::Config for Runtime { type RuntimeEvent = RuntimeEvent; type WeightInfo = weights::runtime_parachains_paras::WeightInfo; type UnsignedPriority = ParasUnsignedPriority; + type QueueFootprinter = ParaInclusion; type NextSessionRotation = Babe; } parameter_types! { - pub const FirstMessageFactorPercent: u64 = 100; + /// Amount of weight that can be spent per block to service messages. + /// + /// # WARNING + /// + /// This is not a good value for para-chains since the `Scheduler` already uses up to 80% block weight. + pub MessageQueueServiceWeight: Weight = Perbill::from_percent(20) * BlockWeights::get().max_block; + pub const MessageQueueHeapSize: u32 = 65_536; + pub const MessageQueueMaxStale: u32 = 16; +} + +/// Message processor to handle any messages that were enqueued into the `MessageQueue` pallet. +pub struct MessageProcessor; +impl ProcessMessage for MessageProcessor { + type Origin = AggregateMessageOrigin; + + fn process_message( + message: &[u8], + origin: Self::Origin, + meter: &mut WeightMeter, + ) -> Result { + let para = match origin { + AggregateMessageOrigin::Ump(UmpQueueId::Para(para)) => para, + }; + xcm_builder::ProcessXcmMessage::< + Junction, + xcm_executor::XcmExecutor, + RuntimeCall, + >::process_message(message, Junction::Parachain(para.into()), meter) + } } -impl parachains_ump::Config for Runtime { +impl pallet_message_queue::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type UmpSink = - crate::parachains_ump::XcmSink, Runtime>; - type FirstMessageFactorPercent = FirstMessageFactorPercent; - type ExecuteOverweightOrigin = EnsureRoot; - type WeightInfo = weights::runtime_parachains_ump::WeightInfo; + type Size = u32; + type HeapSize = MessageQueueHeapSize; + type MaxStale = MessageQueueMaxStale; + type ServiceWeight = MessageQueueServiceWeight; + #[cfg(not(feature = "runtime-benchmarks"))] + type MessageProcessor = MessageProcessor; + #[cfg(feature = "runtime-benchmarks")] + type MessageProcessor = + pallet_message_queue::mock_helpers::NoopMessageProcessor; + type QueueChangeHandler = ParaInclusion; + type WeightInfo = weights::pallet_message_queue::WeightInfo; } impl parachains_dmp::Config for Runtime {} @@ -1411,7 +1451,6 @@ construct_runtime! { Paras: parachains_paras::{Pallet, Call, Storage, Event, Config, ValidateUnsigned} = 56, Initializer: parachains_initializer::{Pallet, Call, Storage} = 57, Dmp: parachains_dmp::{Pallet, Storage} = 58, - Ump: parachains_ump::{Pallet, Call, Storage, Event} = 59, Hrmp: parachains_hrmp::{Pallet, Call, Storage, Event, Config} = 60, ParaSessionInfo: parachains_session_info::{Pallet, Storage} = 61, ParasDisputes: parachains_disputes::{Pallet, Call, Storage, Event} = 62, @@ -1425,6 +1464,9 @@ construct_runtime! { // Pallet for sending XCM. XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event, Origin, Config} = 99, + + // Generalized message queue + MessageQueue: pallet_message_queue::{Pallet, Call, Storage, Event} = 100, } } @@ -1485,7 +1527,12 @@ pub mod migrations { ); /// Unreleased migrations. Add new ones here: - pub type Unreleased = SetStorageVersions; + pub type Unreleased = ( + SetStorageVersions, + // Remove UMP dispatch queue + parachains_configuration::migration::v6::MigrateToV6, + ump_migrations::UpdateUmpLimits, + ); /// Migrations that set `StorageVersion`s we missed to set. pub struct SetStorageVersions; @@ -1510,6 +1557,24 @@ pub mod migrations { } } +/// Helpers to configure the ump migrations. +pub mod ump_migrations { + use runtime_parachains::configuration::migration_ump; + + pub const MAX_UPWARD_QUEUE_SIZE: u32 = 4 * 1024 * 1024; + pub const MAX_UPWARD_QUEUE_COUNT: u32 = 699050; + pub const MAX_UPWARD_MESSAGE_SIZE: u32 = (1 << 16) - 5; // Checked in test `max_upward_message_size`. + pub const MAX_UPWARD_MESSAGE_NUM_PER_CANDIDATE: u32 = 128; + + pub type UpdateUmpLimits = migration_ump::latest::ScheduleConfigUpdate< + super::Runtime, + MAX_UPWARD_QUEUE_SIZE, + MAX_UPWARD_QUEUE_COUNT, + MAX_UPWARD_MESSAGE_SIZE, + MAX_UPWARD_MESSAGE_NUM_PER_CANDIDATE, + >; +} + /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; @@ -1525,13 +1590,9 @@ pub type Executive = frame_executive::Executive< /// The payload being signed in the transactions. pub type SignedPayload = generic::SignedPayload; -#[cfg(feature = "runtime-benchmarks")] -#[macro_use] -extern crate frame_benchmarking; - #[cfg(feature = "runtime-benchmarks")] mod benches { - define_benchmarks!( + frame_benchmarking::define_benchmarks!( // Polkadot // NOTE: Make sure to prefix these with `runtime_common::` so // that the path resolves correctly in the generated file. @@ -1544,10 +1605,10 @@ mod benches { [runtime_parachains::hrmp, Hrmp] [runtime_parachains::disputes, ParasDisputes] [runtime_parachains::disputes::slashing, ParasSlashing] + [runtime_parachains::inclusion, ParaInclusion] [runtime_parachains::initializer, Initializer] [runtime_parachains::paras_inherent, ParaInherent] [runtime_parachains::paras, Paras] - [runtime_parachains::ump, Ump] // Substrate [pallet_balances, Balances] [pallet_balances, NisCounterpartBalances] @@ -1563,6 +1624,7 @@ mod benches { [pallet_identity, Identity] [pallet_im_online, ImOnline] [pallet_indices, Indices] + [pallet_message_queue, MessageQueue] [pallet_multisig, Multisig] [pallet_nomination_pools, NominationPoolsBench::] [pallet_offences, OffencesBench::] diff --git a/runtime/kusama/src/tests.rs b/runtime/kusama/src/tests.rs index b2dd5759f3a9..65c1c0368f78 100644 --- a/runtime/kusama/src/tests.rs +++ b/runtime/kusama/src/tests.rs @@ -146,3 +146,11 @@ fn nominator_limit() { fn call_size() { RuntimeCall::assert_size_under(230); } + +#[test] +fn max_upward_message_size() { + assert_eq!( + ump_migrations::MAX_UPWARD_MESSAGE_SIZE, + pallet_message_queue::MaxMessageLenOf::::get() + ); +} diff --git a/runtime/kusama/src/weights/mod.rs b/runtime/kusama/src/weights/mod.rs index 49c4486c178a..433e35a9c225 100644 --- a/runtime/kusama/src/weights/mod.rs +++ b/runtime/kusama/src/weights/mod.rs @@ -33,6 +33,7 @@ pub mod pallet_identity; pub mod pallet_im_online; pub mod pallet_indices; pub mod pallet_membership; +pub mod pallet_message_queue; pub mod pallet_multisig; pub mod pallet_nis; pub mod pallet_nomination_pools; @@ -60,8 +61,8 @@ pub mod runtime_parachains_configuration; pub mod runtime_parachains_disputes; pub mod runtime_parachains_disputes_slashing; pub mod runtime_parachains_hrmp; +pub mod runtime_parachains_inclusion; pub mod runtime_parachains_initializer; pub mod runtime_parachains_paras; pub mod runtime_parachains_paras_inherent; -pub mod runtime_parachains_ump; pub mod xcm; diff --git a/runtime/kusama/src/weights/pallet_message_queue.rs b/runtime/kusama/src/weights/pallet_message_queue.rs new file mode 100644 index 000000000000..03b0ac4dedde --- /dev/null +++ b/runtime/kusama/src/weights/pallet_message_queue.rs @@ -0,0 +1,176 @@ +// Copyright 2017-2022 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . +//! Autogenerated weights for `pallet_message_queue` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-02-28, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `i9`, CPU: `13th Gen Intel(R) Core(TM) i9-13900K` +//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: Some("kusama-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot +// benchmark +// pallet +// --chain=kusama-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet-message-queue +// --extrinsic=* +// --heap-pages=4096 +// --header=file_header.txt +// --output +// runtime/kusama/src/weights/pallet_message_queue.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for `pallet_message_queue`. +pub struct WeightInfo(PhantomData); +impl pallet_message_queue::WeightInfo for WeightInfo { + /// Storage: MessageQueue ServiceHead (r:1 w:0) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:2 w:2) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + fn ready_ring_knit() -> Weight { + // Proof Size summary in bytes: + // Measured: `837` + // Estimated: `5554` + // Minimum execution time: 5_669 nanoseconds. + Weight::from_parts(5_925_000, 0) + .saturating_add(Weight::from_parts(0, 5554)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: MessageQueue BookStateFor (r:2 w:2) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue ServiceHead (r:1 w:1) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + fn ready_ring_unknit() -> Weight { + // Proof Size summary in bytes: + // Measured: `837` + // Estimated: `5554` + // Minimum execution time: 5_604 nanoseconds. + Weight::from_parts(5_993_000, 0) + .saturating_add(Weight::from_parts(0, 5554)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + fn service_queue_base() -> Weight { + // Proof Size summary in bytes: + // Measured: `576` + // Estimated: `2527` + // Minimum execution time: 2_104 nanoseconds. + Weight::from_parts(2_241_000, 0) + .saturating_add(Weight::from_parts(0, 2527)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: MessageQueue Pages (r:1 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn service_page_base_completion() -> Weight { + // Proof Size summary in bytes: + // Measured: `648` + // Estimated: `68060` + // Minimum execution time: 3_175 nanoseconds. + Weight::from_parts(3_289_000, 0) + .saturating_add(Weight::from_parts(0, 68060)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: MessageQueue Pages (r:1 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn service_page_base_no_completion() -> Weight { + // Proof Size summary in bytes: + // Measured: `648` + // Estimated: `68060` + // Minimum execution time: 3_198 nanoseconds. + Weight::from_parts(3_308_000, 0) + .saturating_add(Weight::from_parts(0, 68060)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + fn service_page_item() -> Weight { + // Proof Size summary in bytes: + // Measured: `969` + // Estimated: `0` + // Minimum execution time: 46_864 nanoseconds. + Weight::from_parts(47_073_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: MessageQueue ServiceHead (r:1 w:1) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:1 w:0) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + fn bump_service_head() -> Weight { + // Proof Size summary in bytes: + // Measured: `712` + // Estimated: `3027` + // Minimum execution time: 3_552 nanoseconds. + Weight::from_parts(3_710_000, 0) + .saturating_add(Weight::from_parts(0, 3027)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:1 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn reap_page() -> Weight { + // Proof Size summary in bytes: + // Measured: `66857` + // Estimated: `70587` + // Minimum execution time: 38_491 nanoseconds. + Weight::from_parts(46_983_000, 0) + .saturating_add(Weight::from_parts(0, 70587)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:1 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn execute_overweight_page_removed() -> Weight { + // Proof Size summary in bytes: + // Measured: `66857` + // Estimated: `70587` + // Minimum execution time: 88_529 nanoseconds. + Weight::from_parts(144_649_000, 0) + .saturating_add(Weight::from_parts(0, 70587)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:1 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn execute_overweight_page_updated() -> Weight { + // Proof Size summary in bytes: + // Measured: `66857` + // Estimated: `70587` + // Minimum execution time: 79_494 nanoseconds. + Weight::from_parts(84_895_000, 0) + .saturating_add(Weight::from_parts(0, 70587)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } +} diff --git a/runtime/kusama/src/weights/runtime_parachains_inclusion.rs b/runtime/kusama/src/weights/runtime_parachains_inclusion.rs new file mode 100644 index 000000000000..dcf4494dac56 --- /dev/null +++ b/runtime/kusama/src/weights/runtime_parachains_inclusion.rs @@ -0,0 +1,66 @@ +// Copyright 2017-2022 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . +//! Autogenerated weights for `runtime_parachains::inclusion` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-02-23, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `i9`, CPU: `13th Gen Intel(R) Core(TM) i9-13900K` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("kusama-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot +// benchmark +// pallet +// --chain=kusama-dev +// --steps=50 +// --repeat=20 +// --pallet=runtime_parachains::inclusion +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --header=./file_header.txt +// --output=./runtime/kusama/src/weights + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for `runtime_parachains::inclusion`. +pub struct WeightInfo(PhantomData); +impl runtime_parachains::inclusion::WeightInfo for WeightInfo { + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:1 w:999) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + /// The range of component `i` is `[1, 1000]`. + fn receive_upward_messages(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `51490` + // Estimated: `70587` + // Minimum execution time: 47_556 nanoseconds. + Weight::from_parts(48_839_000, 0) + .saturating_add(Weight::from_parts(0, 70587)) + // Standard Error: 49_019 + .saturating_add(Weight::from_parts(43_136_059, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) + } +} diff --git a/runtime/kusama/src/weights/runtime_parachains_ump.rs b/runtime/kusama/src/weights/runtime_parachains_ump.rs deleted file mode 100644 index fce7c6c3cf22..000000000000 --- a/runtime/kusama/src/weights/runtime_parachains_ump.rs +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -//! Autogenerated weights for `runtime_parachains::ump` -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-04-28, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm6`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("kusama-dev"), DB CACHE: 1024 - -// Executed Command: -// ./target/production/polkadot -// benchmark -// pallet -// --chain=kusama-dev -// --steps=50 -// --repeat=20 -// --pallet=runtime_parachains::ump -// --extrinsic=* -// --execution=wasm -// --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/kusama/src/weights/runtime_parachains_ump.rs - -#![cfg_attr(rustfmt, rustfmt_skip)] -#![allow(unused_parens)] -#![allow(unused_imports)] -#![allow(missing_docs)] - -use frame_support::{traits::Get, weights::Weight}; -use core::marker::PhantomData; - -/// Weight functions for `runtime_parachains::ump`. -pub struct WeightInfo(PhantomData); -impl runtime_parachains::ump::WeightInfo for WeightInfo { - /// The range of component `s` is `[0, 51200]`. - fn process_upward_message(s: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 5_945_000 picoseconds. - Weight::from_parts(6_047_000, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 8 - .saturating_add(Weight::from_parts(1_364, 0).saturating_mul(s.into())) - } - /// Storage: Ump NeedsDispatch (r:1 w:1) - /// Proof Skipped: Ump NeedsDispatch (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Ump NextDispatchRoundStartWith (r:1 w:1) - /// Proof Skipped: Ump NextDispatchRoundStartWith (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Ump RelayDispatchQueues (r:0 w:1) - /// Proof Skipped: Ump RelayDispatchQueues (max_values: None, max_size: None, mode: Measured) - /// Storage: Ump RelayDispatchQueueSize (r:0 w:1) - /// Proof Skipped: Ump RelayDispatchQueueSize (max_values: None, max_size: None, mode: Measured) - fn clean_ump_after_outgoing() -> Weight { - // Proof Size summary in bytes: - // Measured: `206` - // Estimated: `1691` - // Minimum execution time: 9_308_000 picoseconds. - Weight::from_parts(9_501_000, 0) - .saturating_add(Weight::from_parts(0, 1691)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(4)) - } - /// Storage: Ump Overweight (r:1 w:1) - /// Proof Skipped: Ump Overweight (max_values: None, max_size: None, mode: Measured) - /// Storage: Ump CounterForOverweight (r:1 w:1) - /// Proof: Ump CounterForOverweight (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - fn service_overweight() -> Weight { - // Proof Size summary in bytes: - // Measured: `223` - // Estimated: `3688` - // Minimum execution time: 23_650_000 picoseconds. - Weight::from_parts(23_963_000, 0) - .saturating_add(Weight::from_parts(0, 3688)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(2)) - } -} diff --git a/runtime/parachains/Cargo.toml b/runtime/parachains/Cargo.toml index a097fe4fa161..e1b5b5b1de59 100644 --- a/runtime/parachains/Cargo.toml +++ b/runtime/parachains/Cargo.toml @@ -30,6 +30,7 @@ pallet-authority-discovery = { git = "https://github.com/paritytech/substrate", pallet-authorship = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-babe = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-message-queue = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-session = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-staking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -80,9 +81,11 @@ std = [ "sp-runtime/std", "sp-session/std", "sp-staking/std", + "pallet-authority-discovery/std", "pallet-authorship/std", "pallet-babe/std", "pallet-balances/std", + "pallet-message-queue/std", "pallet-session/std", "pallet-staking/std", "pallet-timestamp/std", @@ -97,15 +100,23 @@ runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", + "pallet-babe/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-message-queue/runtime-benchmarks", "pallet-staking/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks", + "pallet-vesting/runtime-benchmarks", "primitives/runtime-benchmarks", "static_assertions", "sp-application-crypto", ] try-runtime = [ "frame-support/try-runtime", + "pallet-authority-discovery/try-runtime", "pallet-authorship/try-runtime", + "pallet-babe/try-runtime", "pallet-balances/try-runtime", + "pallet-message-queue/try-runtime", "pallet-session/try-runtime", "pallet-staking/try-runtime", "pallet-timestamp/try-runtime", diff --git a/runtime/parachains/src/configuration.rs b/runtime/parachains/src/configuration.rs index 380ae4770851..a5de197b969d 100644 --- a/runtime/parachains/src/configuration.rs +++ b/runtime/parachains/src/configuration.rs @@ -18,8 +18,8 @@ //! //! Configuration can change only at session boundaries and is buffered until then. -use crate::shared; -use frame_support::{pallet_prelude::*, weights::constants::WEIGHT_REF_TIME_PER_MILLIS}; +use crate::{inclusion::MAX_UPWARD_MESSAGE_SIZE_BOUND, shared}; +use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use parity_scale_codec::{Decode, Encode}; use polkadot_parachain::primitives::{MAX_HORIZONTAL_MESSAGE_NUM, MAX_UPWARD_MESSAGE_NUM}; @@ -36,9 +36,10 @@ mod tests; #[cfg(feature = "runtime-benchmarks")] mod benchmarking; -pub use pallet::*; - pub mod migration; +pub mod migration_ump; + +pub use pallet::*; const LOG_TARGET: &str = "runtime::configuration"; @@ -132,11 +133,6 @@ pub struct HostConfiguration { /// decide to do with its PoV so this value in practice will be picked as a fraction of the PoV /// size. pub max_downward_message_size: u32, - /// The amount of weight we wish to devote to the processing the dispatchable upward messages - /// stage. - /// - /// NOTE that this is a soft limit and could be exceeded. - pub ump_service_total_weight: Weight, /// The maximum number of outbound HRMP channels a parachain is allowed to open. pub hrmp_max_parachain_outbound_channels: u32, /// The maximum number of outbound HRMP channels a parathread is allowed to open. @@ -214,9 +210,6 @@ pub struct HostConfiguration { pub needed_approvals: u32, /// The number of samples to do of the `RelayVRFModulo` approval assignment criterion. pub relay_vrf_modulo_samples: u32, - /// The maximum amount of weight any individual upward message may consume. Messages above this - /// weight go into the overweight queue and may only be serviced explicitly. - pub ump_max_individual_weight: Weight, /// This flag controls whether PVF pre-checking is enabled. /// /// If the flag is false, the behavior should be exactly the same as prior. Specifically, the @@ -278,7 +271,6 @@ impl> Default for HostConfiguration> Default for HostConfiguration crate::ump::MAX_UPWARD_MESSAGE_SIZE_BOUND { + if self.max_upward_message_size > crate::inclusion::MAX_UPWARD_MESSAGE_SIZE_BOUND { return Err(MaxUpwardMessageSizeExceeded { max_message_size: self.max_upward_message_size, }) @@ -441,7 +429,7 @@ where /// This function panics if the configuration is inconsistent. pub fn panic_if_not_consistent(&self) { if let Err(err) = self.check_consistency() { - panic!("Host configuration is inconsistent: {:?}", err); + panic!("Host configuration is inconsistent: {:?}\nCfg:\n{:#?}", err, self); } } } @@ -865,6 +853,8 @@ pub mod pallet { ))] pub fn set_max_upward_queue_size(origin: OriginFor, new: u32) -> DispatchResult { ensure_root(origin)?; + ensure!(new <= MAX_UPWARD_MESSAGE_SIZE_BOUND, Error::::InvalidNewValue); + Self::schedule_config_update(|config| { config.max_upward_queue_size = new; }) @@ -883,19 +873,6 @@ pub mod pallet { }) } - /// Sets the soft limit for the phase of dispatching dispatchable upward messages. - #[pallet::call_index(26)] - #[pallet::weight(( - T::WeightInfo::set_config_with_weight(), - DispatchClass::Operational, - ))] - pub fn set_ump_service_total_weight(origin: OriginFor, new: Weight) -> DispatchResult { - ensure_root(origin)?; - Self::schedule_config_update(|config| { - config.ump_service_total_weight = new; - }) - } - /// Sets the maximum size of an upward message that can be sent by a candidate. #[pallet::call_index(27)] #[pallet::weight(( @@ -1083,19 +1060,6 @@ pub mod pallet { }) } - /// Sets the maximum amount of weight any individual upward message may consume. - #[pallet::call_index(40)] - #[pallet::weight(( - T::WeightInfo::set_config_with_weight(), - DispatchClass::Operational, - ))] - pub fn set_ump_max_individual_weight(origin: OriginFor, new: Weight) -> DispatchResult { - ensure_root(origin)?; - Self::schedule_config_update(|config| { - config.ump_max_individual_weight = new; - }) - } - /// Enable or disable PVF pre-checking. Consult the field documentation prior executing. #[pallet::call_index(41)] #[pallet::weight(( @@ -1283,7 +1247,7 @@ impl Pallet { // duplicated code (making this function to show up in the top of heaviest functions) only for // the sake of essentially avoiding an indirect call. Doesn't worth it. #[inline(never)] - fn schedule_config_update( + pub(crate) fn schedule_config_update( updater: impl FnOnce(&mut HostConfiguration), ) -> DispatchResult { let mut pending_configs = >::get(); diff --git a/runtime/parachains/src/configuration/benchmarking.rs b/runtime/parachains/src/configuration/benchmarking.rs index 0b8b09fe729e..ef8fafd91c96 100644 --- a/runtime/parachains/src/configuration/benchmarking.rs +++ b/runtime/parachains/src/configuration/benchmarking.rs @@ -27,8 +27,6 @@ benchmarks! { set_config_with_option_u32 {}: set_max_validators(RawOrigin::Root, Some(10)) - set_config_with_weight {}: set_ump_service_total_weight(RawOrigin::Root, Weight::from_parts(3_000_000, 0)) - set_hrmp_open_request_ttl {}: { Err(BenchmarkError::Override( BenchmarkResult::from_weight(T::BlockWeights::get().max_block) diff --git a/runtime/parachains/src/configuration/migration.rs b/runtime/parachains/src/configuration/migration.rs index 3aef1761117b..c41d4f3055b7 100644 --- a/runtime/parachains/src/configuration/migration.rs +++ b/runtime/parachains/src/configuration/migration.rs @@ -16,11 +16,8 @@ //! A module that is responsible for migration of storage. -use crate::configuration::{self, ActiveConfig, Config, Pallet, PendingConfigs, MAX_POV_SIZE}; -use frame_support::{pallet_prelude::*, traits::StorageVersion, weights::Weight}; -use frame_system::pallet_prelude::BlockNumberFor; -use primitives::vstaging::AsyncBackingParams; -use sp_std::vec::Vec; +use frame_support::traits::StorageVersion; +use primitives::ExecutorParams; /// The current storage version. /// @@ -29,376 +26,10 @@ use sp_std::vec::Vec; /// v2-v3: /// v3-v4: /// v4-v5: -/// + -/// + -pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(5); +/// + +/// + +/// v5-v6: (remove UMP dispatch queue) +pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(6); -pub mod v5 { - use super::*; - use frame_support::{traits::OnRuntimeUpgrade, weights::constants::WEIGHT_REF_TIME_PER_MILLIS}; - use primitives::{Balance, SessionIndex}; - #[cfg(feature = "try-runtime")] - use sp_std::prelude::*; - - // Copied over from configuration.rs @ de9e147695b9f1be8bd44e07861a31e483c8343a and removed - // all the comments, and changed the Weight struct to OldWeight - #[derive(parity_scale_codec::Encode, parity_scale_codec::Decode, Debug, Clone)] - pub struct OldHostConfiguration { - pub max_code_size: u32, - pub max_head_data_size: u32, - pub max_upward_queue_count: u32, - pub max_upward_queue_size: u32, - pub max_upward_message_size: u32, - pub max_upward_message_num_per_candidate: u32, - pub hrmp_max_message_num_per_candidate: u32, - pub validation_upgrade_cooldown: BlockNumber, - pub validation_upgrade_delay: BlockNumber, - pub max_pov_size: u32, - pub max_downward_message_size: u32, - pub ump_service_total_weight: Weight, - pub hrmp_max_parachain_outbound_channels: u32, - pub hrmp_max_parathread_outbound_channels: u32, - pub hrmp_sender_deposit: Balance, - pub hrmp_recipient_deposit: Balance, - pub hrmp_channel_max_capacity: u32, - pub hrmp_channel_max_total_size: u32, - pub hrmp_max_parachain_inbound_channels: u32, - pub hrmp_max_parathread_inbound_channels: u32, - pub hrmp_channel_max_message_size: u32, - pub code_retention_period: BlockNumber, - pub parathread_cores: u32, - pub parathread_retries: u32, - pub group_rotation_frequency: BlockNumber, - pub chain_availability_period: BlockNumber, - pub thread_availability_period: BlockNumber, - pub scheduling_lookahead: u32, - pub max_validators_per_core: Option, - pub max_validators: Option, - pub dispute_period: SessionIndex, - pub dispute_post_conclusion_acceptance_period: BlockNumber, - pub dispute_conclusion_by_time_out_period: BlockNumber, - pub no_show_slots: u32, - pub n_delay_tranches: u32, - pub zeroth_delay_tranche_width: u32, - pub needed_approvals: u32, - pub relay_vrf_modulo_samples: u32, - pub ump_max_individual_weight: Weight, - pub pvf_checking_enabled: bool, - pub pvf_voting_ttl: SessionIndex, - pub minimum_validation_upgrade_delay: BlockNumber, - } - - impl> Default for OldHostConfiguration { - fn default() -> Self { - Self { - group_rotation_frequency: 1u32.into(), - chain_availability_period: 1u32.into(), - thread_availability_period: 1u32.into(), - no_show_slots: 1u32.into(), - validation_upgrade_cooldown: Default::default(), - validation_upgrade_delay: Default::default(), - code_retention_period: Default::default(), - max_code_size: Default::default(), - max_pov_size: Default::default(), - max_head_data_size: Default::default(), - parathread_cores: Default::default(), - parathread_retries: Default::default(), - scheduling_lookahead: Default::default(), - max_validators_per_core: Default::default(), - max_validators: None, - dispute_period: 6, - dispute_post_conclusion_acceptance_period: 100.into(), - dispute_conclusion_by_time_out_period: 200.into(), - n_delay_tranches: Default::default(), - zeroth_delay_tranche_width: Default::default(), - needed_approvals: Default::default(), - relay_vrf_modulo_samples: Default::default(), - max_upward_queue_count: Default::default(), - max_upward_queue_size: Default::default(), - max_downward_message_size: Default::default(), - ump_service_total_weight: Default::default(), - max_upward_message_size: Default::default(), - max_upward_message_num_per_candidate: Default::default(), - hrmp_sender_deposit: Default::default(), - hrmp_recipient_deposit: Default::default(), - hrmp_channel_max_capacity: Default::default(), - hrmp_channel_max_total_size: Default::default(), - hrmp_max_parachain_inbound_channels: Default::default(), - hrmp_max_parathread_inbound_channels: Default::default(), - hrmp_channel_max_message_size: Default::default(), - hrmp_max_parachain_outbound_channels: Default::default(), - hrmp_max_parathread_outbound_channels: Default::default(), - hrmp_max_message_num_per_candidate: Default::default(), - ump_max_individual_weight: Weight::from_parts( - 20u64 * WEIGHT_REF_TIME_PER_MILLIS, - MAX_POV_SIZE as u64, - ), - pvf_checking_enabled: false, - pvf_voting_ttl: 2u32.into(), - minimum_validation_upgrade_delay: 2.into(), - } - } - } - - pub struct MigrateToV5(sp_std::marker::PhantomData); - impl OnRuntimeUpgrade for MigrateToV5 { - #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, &'static str> { - log::trace!(target: crate::configuration::LOG_TARGET, "Running pre_upgrade()"); - - ensure!(StorageVersion::get::>() == 4, "The migration requires version 4"); - Ok(Vec::new()) - } - - fn on_runtime_upgrade() -> Weight { - if StorageVersion::get::>() == 4 { - let weight_consumed = migrate_to_v5::(); - - log::info!(target: configuration::LOG_TARGET, "MigrateToV5 executed successfully"); - STORAGE_VERSION.put::>(); - - weight_consumed - } else { - log::warn!(target: configuration::LOG_TARGET, "MigrateToV5 should be removed."); - T::DbWeight::get().reads(1) - } - } - - #[cfg(feature = "try-runtime")] - fn post_upgrade(_state: Vec) -> Result<(), &'static str> { - log::trace!(target: crate::configuration::LOG_TARGET, "Running post_upgrade()"); - ensure!( - StorageVersion::get::>() == STORAGE_VERSION, - "Storage version should be 5 after the migration" - ); - - Ok(()) - } - } -} - -fn migrate_to_v5() -> Weight { - // Unusual formatting is justified: - // - make it easier to verify that fields assign what they supposed to assign. - // - this code is transient and will be removed after all migrations are done. - // - this code is important enough to optimize for legibility sacrificing consistency. - #[rustfmt::skip] - let translate = - |pre: v5::OldHostConfiguration>| -> -configuration::HostConfiguration> - { - super::HostConfiguration { -max_code_size : pre.max_code_size, -max_head_data_size : pre.max_head_data_size, -max_upward_queue_count : pre.max_upward_queue_count, -max_upward_queue_size : pre.max_upward_queue_size, -max_upward_message_size : pre.max_upward_message_size, -max_upward_message_num_per_candidate : pre.max_upward_message_num_per_candidate, -hrmp_max_message_num_per_candidate : pre.hrmp_max_message_num_per_candidate, -validation_upgrade_cooldown : pre.validation_upgrade_cooldown, -validation_upgrade_delay : pre.validation_upgrade_delay, -max_pov_size : pre.max_pov_size, -max_downward_message_size : pre.max_downward_message_size, -ump_service_total_weight : pre.ump_service_total_weight, -hrmp_max_parachain_outbound_channels : pre.hrmp_max_parachain_outbound_channels, -hrmp_max_parathread_outbound_channels : pre.hrmp_max_parathread_outbound_channels, -hrmp_sender_deposit : pre.hrmp_sender_deposit, -hrmp_recipient_deposit : pre.hrmp_recipient_deposit, -hrmp_channel_max_capacity : pre.hrmp_channel_max_capacity, -hrmp_channel_max_total_size : pre.hrmp_channel_max_total_size, -hrmp_max_parachain_inbound_channels : pre.hrmp_max_parachain_inbound_channels, -hrmp_max_parathread_inbound_channels : pre.hrmp_max_parathread_inbound_channels, -hrmp_channel_max_message_size : pre.hrmp_channel_max_message_size, -code_retention_period : pre.code_retention_period, -parathread_cores : pre.parathread_cores, -parathread_retries : pre.parathread_retries, -group_rotation_frequency : pre.group_rotation_frequency, -chain_availability_period : pre.chain_availability_period, -thread_availability_period : pre.thread_availability_period, -scheduling_lookahead : pre.scheduling_lookahead, -max_validators_per_core : pre.max_validators_per_core, -max_validators : pre.max_validators, -dispute_period : pre.dispute_period, -dispute_post_conclusion_acceptance_period: pre.dispute_post_conclusion_acceptance_period, -no_show_slots : pre.no_show_slots, -n_delay_tranches : pre.n_delay_tranches, -zeroth_delay_tranche_width : pre.zeroth_delay_tranche_width, -needed_approvals : pre.needed_approvals, -relay_vrf_modulo_samples : pre.relay_vrf_modulo_samples, -ump_max_individual_weight : pre.ump_max_individual_weight, -pvf_checking_enabled : pre.pvf_checking_enabled, -pvf_voting_ttl : pre.pvf_voting_ttl, -minimum_validation_upgrade_delay : pre.minimum_validation_upgrade_delay, - -// Default values are zeroes, thus it's ensured allowed ancestry never crosses the upgrade block. -async_backing_params : AsyncBackingParams { max_candidate_depth: 0, allowed_ancestry_len: 0 }, - -// Default executor parameters set is empty -executor_params : Default::default(), - } - }; - - if let Err(_) = ActiveConfig::::translate(|pre| pre.map(translate)) { - // `Err` is returned when the pre-migration type cannot be deserialized. This - // cannot happen if the migration runs correctly, i.e. against the expected version. - // - // This happening almost surely will lead to a panic somewhere else. Corruption seems - // to be unlikely to be caused by this. So we just log. Maybe it'll work out still? - log::error!( - target: configuration::LOG_TARGET, - "unexpected error when performing translation of the active configuration during storage upgrade to v5." - ); - } - - if let Err(_) = PendingConfigs::::translate(|pre| { - pre.map( - |v: Vec<(primitives::SessionIndex, v5::OldHostConfiguration>)>| { - v.into_iter() - .map(|(session, config)| (session, translate(config))) - .collect::>() - }, - ) - }) { - log::error!( - target: configuration::LOG_TARGET, - "unexpected error when performing translation of the pending configuration during storage upgrade to v5." - ); - } - - let num_configs = (PendingConfigs::::get().len() + 1) as u64; - T::DbWeight::get().reads_writes(num_configs, num_configs) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::mock::{new_test_ext, Test}; - use primitives::ExecutorParams; - - #[test] - fn v4_deserialized_from_actual_data() { - // Example how to get new `raw_config`: - // We'll obtain the raw_config at a specified a block - // Steps: - // 1. Go to Polkadot.js -> Developer -> Chain state -> Storage: https://polkadot.js.org/apps/#/chainstate - // 2. Set these parameters: - // 2.1. selected state query: configuration; activeConfig(): PolkadotRuntimeParachainsConfigurationHostConfiguration - // 2.2. blockhash to query at: 0xf89d3ab5312c5f70d396dc59612f0aa65806c798346f9db4b35278baed2e0e53 (the hash of the block) - // 2.3. Note the value of encoded storage key -> 0x06de3d8a54d27e44a9d5ce189618f22db4b49d95320d9021994c850f25b8e385 for the referenced block. - // 2.4. You'll also need the decoded values to update the test. - // 3. Go to Polkadot.js -> Developer -> Chain state -> Raw storage - // 3.1 Enter the encoded storage key and you get the raw config. - - // This exceeds the maximal line width length, but that's fine, since this is not code and - // doesn't need to be read and also leaving it as one line allows to easily copy it. - let raw_config = hex_literal::hex!["0000a000005000000a00000000c8000000c800000a0000000a000000100e0000580200000000500000c800000700e8764817020040011e00000000000000005039278c0400000000000000000000005039278c0400000000000000000000e8030000009001001e00000000000000009001008070000000000000000000000a0000000a0000000a00000001000000010500000001c80000000600000058020000580200000200000059000000000000001e000000280000000700c817a80402004001010200000014000000"]; - - let v4 = v5::OldHostConfiguration::::decode(&mut &raw_config[..]) - .unwrap(); - - // We check only a sample of the values here. If we missed any fields or messed up data types - // that would skew all the fields coming after. - assert_eq!(v4.max_code_size, 10_485_760); - assert_eq!(v4.validation_upgrade_cooldown, 3600); - assert_eq!(v4.max_pov_size, 5_242_880); - assert_eq!(v4.hrmp_channel_max_message_size, 102_400); - assert_eq!(v4.n_delay_tranches, 89); - assert_eq!(v4.ump_max_individual_weight, Weight::from_parts(20_000_000_000, 5_242_880)); - assert_eq!(v4.minimum_validation_upgrade_delay, 20); - } - - #[test] - fn test_migrate_to_v5() { - // Host configuration has lots of fields. However, in this migration we only remove one field. - // The most important part to check are a couple of the last fields. We also pick - // extra fields to check arbitrarily, e.g. depending on their position (i.e. the middle) and - // also their type. - // - // We specify only the picked fields and the rest should be provided by the `Default` - // implementation. That implementation is copied over between the two types and should work - // fine. - let v4 = v5::OldHostConfiguration:: { - ump_max_individual_weight: Weight::from_parts(0x71616e6f6e0au64, 0x71616e6f6e0au64), - needed_approvals: 69, - thread_availability_period: 55, - hrmp_recipient_deposit: 1337, - max_pov_size: 1111, - chain_availability_period: 33, - minimum_validation_upgrade_delay: 20, - ..Default::default() - }; - - let mut pending_configs = Vec::new(); - pending_configs.push((100, v4.clone())); - pending_configs.push((300, v4.clone())); - - new_test_ext(Default::default()).execute_with(|| { - // Implant the v4 version in the state. - frame_support::storage::unhashed::put_raw( - &configuration::ActiveConfig::::hashed_key(), - &v4.encode(), - ); - frame_support::storage::unhashed::put_raw( - &configuration::PendingConfigs::::hashed_key(), - &pending_configs.encode(), - ); - - migrate_to_v5::(); - - let v5 = configuration::ActiveConfig::::get(); - let mut configs_to_check = configuration::PendingConfigs::::get(); - configs_to_check.push((0, v5.clone())); - - for (_, v4) in configs_to_check { - #[rustfmt::skip] - { - assert_eq!(v4.max_code_size , v5.max_code_size); - assert_eq!(v4.max_head_data_size , v5.max_head_data_size); - assert_eq!(v4.max_upward_queue_count , v5.max_upward_queue_count); - assert_eq!(v4.max_upward_queue_size , v5.max_upward_queue_size); - assert_eq!(v4.max_upward_message_size , v5.max_upward_message_size); - assert_eq!(v4.max_upward_message_num_per_candidate , v5.max_upward_message_num_per_candidate); - assert_eq!(v4.hrmp_max_message_num_per_candidate , v5.hrmp_max_message_num_per_candidate); - assert_eq!(v4.validation_upgrade_cooldown , v5.validation_upgrade_cooldown); - assert_eq!(v4.validation_upgrade_delay , v5.validation_upgrade_delay); - assert_eq!(v4.max_pov_size , v5.max_pov_size); - assert_eq!(v4.max_downward_message_size , v5.max_downward_message_size); - assert_eq!(v4.ump_service_total_weight , v5.ump_service_total_weight); - assert_eq!(v4.hrmp_max_parachain_outbound_channels , v5.hrmp_max_parachain_outbound_channels); - assert_eq!(v4.hrmp_max_parathread_outbound_channels , v5.hrmp_max_parathread_outbound_channels); - assert_eq!(v4.hrmp_sender_deposit , v5.hrmp_sender_deposit); - assert_eq!(v4.hrmp_recipient_deposit , v5.hrmp_recipient_deposit); - assert_eq!(v4.hrmp_channel_max_capacity , v5.hrmp_channel_max_capacity); - assert_eq!(v4.hrmp_channel_max_total_size , v5.hrmp_channel_max_total_size); - assert_eq!(v4.hrmp_max_parachain_inbound_channels , v5.hrmp_max_parachain_inbound_channels); - assert_eq!(v4.hrmp_max_parathread_inbound_channels , v5.hrmp_max_parathread_inbound_channels); - assert_eq!(v4.hrmp_channel_max_message_size , v5.hrmp_channel_max_message_size); - assert_eq!(v4.code_retention_period , v5.code_retention_period); - assert_eq!(v4.parathread_cores , v5.parathread_cores); - assert_eq!(v4.parathread_retries , v5.parathread_retries); - assert_eq!(v4.group_rotation_frequency , v5.group_rotation_frequency); - assert_eq!(v4.chain_availability_period , v5.chain_availability_period); - assert_eq!(v4.thread_availability_period , v5.thread_availability_period); - assert_eq!(v4.scheduling_lookahead , v5.scheduling_lookahead); - assert_eq!(v4.max_validators_per_core , v5.max_validators_per_core); - assert_eq!(v4.max_validators , v5.max_validators); - assert_eq!(v4.dispute_period , v5.dispute_period); - assert_eq!(v4.no_show_slots , v5.no_show_slots); - assert_eq!(v4.n_delay_tranches , v5.n_delay_tranches); - assert_eq!(v4.zeroth_delay_tranche_width , v5.zeroth_delay_tranche_width); - assert_eq!(v4.needed_approvals , v5.needed_approvals); - assert_eq!(v4.relay_vrf_modulo_samples , v5.relay_vrf_modulo_samples); - assert_eq!(v4.ump_max_individual_weight , v5.ump_max_individual_weight); - assert_eq!(v4.pvf_checking_enabled , v5.pvf_checking_enabled); - assert_eq!(v4.pvf_voting_ttl , v5.pvf_voting_ttl); - assert_eq!(v4.minimum_validation_upgrade_delay , v5.minimum_validation_upgrade_delay); - }; // ; makes this a statement. `rustfmt::skip` cannot be put on an expression. - - // additional checks for async backing. - assert_eq!(v5.async_backing_params.allowed_ancestry_len, 0); - assert_eq!(v5.async_backing_params.max_candidate_depth, 0); - assert_eq!(v5.executor_params, ExecutorParams::new()); - } - }); - } -} +pub mod v5; +pub mod v6; diff --git a/runtime/parachains/src/configuration/migration/v5.rs b/runtime/parachains/src/configuration/migration/v5.rs new file mode 100644 index 000000000000..8cb17408878e --- /dev/null +++ b/runtime/parachains/src/configuration/migration/v5.rs @@ -0,0 +1,504 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! A module that is responsible for migration of storage. + +use crate::configuration::{self, Config, Pallet, MAX_POV_SIZE}; +use frame_support::{ + pallet_prelude::*, + traits::{Defensive, StorageVersion}, + weights::Weight, +}; +use frame_system::pallet_prelude::BlockNumberFor; +use primitives::vstaging::AsyncBackingParams; +use sp_std::vec::Vec; + +use super::*; +use frame_support::{traits::OnRuntimeUpgrade, weights::constants::WEIGHT_REF_TIME_PER_MILLIS}; +use primitives::{Balance, SessionIndex}; +#[cfg(feature = "try-runtime")] +use sp_std::prelude::*; + +// Copied over from configuration.rs @ de9e147695b9f1be8bd44e07861a31e483c8343a and removed +// all the comments, and changed the Weight struct to OldWeight +#[derive(parity_scale_codec::Encode, parity_scale_codec::Decode, Debug, Clone)] +pub struct V4HostConfiguration { + pub max_code_size: u32, + pub max_head_data_size: u32, + pub max_upward_queue_count: u32, + pub max_upward_queue_size: u32, + pub max_upward_message_size: u32, + pub max_upward_message_num_per_candidate: u32, + pub hrmp_max_message_num_per_candidate: u32, + pub validation_upgrade_cooldown: BlockNumber, + pub validation_upgrade_delay: BlockNumber, + pub max_pov_size: u32, + pub max_downward_message_size: u32, + pub ump_service_total_weight: Weight, + pub hrmp_max_parachain_outbound_channels: u32, + pub hrmp_max_parathread_outbound_channels: u32, + pub hrmp_sender_deposit: Balance, + pub hrmp_recipient_deposit: Balance, + pub hrmp_channel_max_capacity: u32, + pub hrmp_channel_max_total_size: u32, + pub hrmp_max_parachain_inbound_channels: u32, + pub hrmp_max_parathread_inbound_channels: u32, + pub hrmp_channel_max_message_size: u32, + pub code_retention_period: BlockNumber, + pub parathread_cores: u32, + pub parathread_retries: u32, + pub group_rotation_frequency: BlockNumber, + pub chain_availability_period: BlockNumber, + pub thread_availability_period: BlockNumber, + pub scheduling_lookahead: u32, + pub max_validators_per_core: Option, + pub max_validators: Option, + pub dispute_period: SessionIndex, + pub dispute_post_conclusion_acceptance_period: BlockNumber, + pub dispute_conclusion_by_time_out_period: BlockNumber, + pub no_show_slots: u32, + pub n_delay_tranches: u32, + pub zeroth_delay_tranche_width: u32, + pub needed_approvals: u32, + pub relay_vrf_modulo_samples: u32, + pub ump_max_individual_weight: Weight, + pub pvf_checking_enabled: bool, + pub pvf_voting_ttl: SessionIndex, + pub minimum_validation_upgrade_delay: BlockNumber, +} + +#[derive(parity_scale_codec::Encode, parity_scale_codec::Decode, Debug, Clone)] +pub struct V5HostConfiguration { + pub max_code_size: u32, + pub max_head_data_size: u32, + pub max_upward_queue_count: u32, + pub max_upward_queue_size: u32, + pub max_upward_message_size: u32, + pub max_upward_message_num_per_candidate: u32, + pub hrmp_max_message_num_per_candidate: u32, + pub validation_upgrade_cooldown: BlockNumber, + pub validation_upgrade_delay: BlockNumber, + pub async_backing_params: AsyncBackingParams, + pub max_pov_size: u32, + pub max_downward_message_size: u32, + pub ump_service_total_weight: Weight, + pub hrmp_max_parachain_outbound_channels: u32, + pub hrmp_max_parathread_outbound_channels: u32, + pub hrmp_sender_deposit: Balance, + pub hrmp_recipient_deposit: Balance, + pub hrmp_channel_max_capacity: u32, + pub hrmp_channel_max_total_size: u32, + pub hrmp_max_parachain_inbound_channels: u32, + pub hrmp_max_parathread_inbound_channels: u32, + pub hrmp_channel_max_message_size: u32, + pub executor_params: ExecutorParams, + pub code_retention_period: BlockNumber, + pub parathread_cores: u32, + pub parathread_retries: u32, + pub group_rotation_frequency: BlockNumber, + pub chain_availability_period: BlockNumber, + pub thread_availability_period: BlockNumber, + pub scheduling_lookahead: u32, + pub max_validators_per_core: Option, + pub max_validators: Option, + pub dispute_period: SessionIndex, + pub dispute_post_conclusion_acceptance_period: BlockNumber, + pub no_show_slots: u32, + pub n_delay_tranches: u32, + pub zeroth_delay_tranche_width: u32, + pub needed_approvals: u32, + pub relay_vrf_modulo_samples: u32, + pub ump_max_individual_weight: Weight, + pub pvf_checking_enabled: bool, + pub pvf_voting_ttl: SessionIndex, + pub minimum_validation_upgrade_delay: BlockNumber, +} + +#[frame_support::storage_alias] +pub(crate) type V4ActiveConfig = + StorageValue, V4HostConfiguration>, OptionQuery>; + +#[frame_support::storage_alias] +pub(crate) type V4PendingConfigs = StorageValue< + Pallet, + Vec<(SessionIndex, V4HostConfiguration>)>, + OptionQuery, +>; + +#[frame_support::storage_alias] +pub(crate) type V5ActiveConfig = + StorageValue, V5HostConfiguration>, OptionQuery>; + +#[frame_support::storage_alias] +pub(crate) type V5PendingConfigs = StorageValue< + Pallet, + Vec<(SessionIndex, V5HostConfiguration>)>, + OptionQuery, +>; + +impl> Default for V4HostConfiguration { + fn default() -> Self { + Self { + group_rotation_frequency: 1u32.into(), + chain_availability_period: 1u32.into(), + thread_availability_period: 1u32.into(), + no_show_slots: 1u32.into(), + validation_upgrade_cooldown: Default::default(), + validation_upgrade_delay: Default::default(), + code_retention_period: Default::default(), + max_code_size: Default::default(), + max_pov_size: Default::default(), + max_head_data_size: Default::default(), + parathread_cores: Default::default(), + parathread_retries: Default::default(), + scheduling_lookahead: Default::default(), + max_validators_per_core: Default::default(), + max_validators: None, + dispute_period: 6, + dispute_post_conclusion_acceptance_period: 100.into(), + dispute_conclusion_by_time_out_period: 200.into(), + n_delay_tranches: Default::default(), + zeroth_delay_tranche_width: Default::default(), + needed_approvals: Default::default(), + relay_vrf_modulo_samples: Default::default(), + max_upward_queue_count: Default::default(), + max_upward_queue_size: Default::default(), + max_downward_message_size: Default::default(), + ump_service_total_weight: Default::default(), + max_upward_message_size: Default::default(), + max_upward_message_num_per_candidate: Default::default(), + hrmp_sender_deposit: Default::default(), + hrmp_recipient_deposit: Default::default(), + hrmp_channel_max_capacity: Default::default(), + hrmp_channel_max_total_size: Default::default(), + hrmp_max_parachain_inbound_channels: Default::default(), + hrmp_max_parathread_inbound_channels: Default::default(), + hrmp_channel_max_message_size: Default::default(), + hrmp_max_parachain_outbound_channels: Default::default(), + hrmp_max_parathread_outbound_channels: Default::default(), + hrmp_max_message_num_per_candidate: Default::default(), + ump_max_individual_weight: Weight::from_parts( + 20u64 * WEIGHT_REF_TIME_PER_MILLIS, + MAX_POV_SIZE as u64, + ), + pvf_checking_enabled: false, + pvf_voting_ttl: 2u32.into(), + minimum_validation_upgrade_delay: 2.into(), + } + } +} + +impl> Default for V5HostConfiguration { + fn default() -> Self { + Self { + group_rotation_frequency: 1u32.into(), + chain_availability_period: 1u32.into(), + thread_availability_period: 1u32.into(), + no_show_slots: 1u32.into(), + validation_upgrade_cooldown: Default::default(), + validation_upgrade_delay: Default::default(), + code_retention_period: Default::default(), + max_code_size: Default::default(), + max_pov_size: Default::default(), + max_head_data_size: Default::default(), + parathread_cores: Default::default(), + parathread_retries: Default::default(), + scheduling_lookahead: Default::default(), + max_validators_per_core: Default::default(), + max_validators: None, + dispute_period: 6, + dispute_post_conclusion_acceptance_period: 100.into(), + n_delay_tranches: Default::default(), + zeroth_delay_tranche_width: Default::default(), + needed_approvals: Default::default(), + relay_vrf_modulo_samples: Default::default(), + max_upward_queue_count: Default::default(), + max_upward_queue_size: Default::default(), + max_downward_message_size: Default::default(), + ump_service_total_weight: Default::default(), + max_upward_message_size: Default::default(), + max_upward_message_num_per_candidate: Default::default(), + hrmp_sender_deposit: Default::default(), + hrmp_recipient_deposit: Default::default(), + hrmp_channel_max_capacity: Default::default(), + hrmp_channel_max_total_size: Default::default(), + hrmp_max_parachain_inbound_channels: Default::default(), + hrmp_max_parathread_inbound_channels: Default::default(), + hrmp_channel_max_message_size: Default::default(), + hrmp_max_parachain_outbound_channels: Default::default(), + hrmp_max_parathread_outbound_channels: Default::default(), + hrmp_max_message_num_per_candidate: Default::default(), + ump_max_individual_weight: Weight::from_parts( + 20u64 * WEIGHT_REF_TIME_PER_MILLIS, + MAX_POV_SIZE as u64, + ), + pvf_checking_enabled: false, + pvf_voting_ttl: 2u32.into(), + minimum_validation_upgrade_delay: 2.into(), + async_backing_params: AsyncBackingParams { + max_candidate_depth: 0, + allowed_ancestry_len: 0, + }, + executor_params: Default::default(), + } + } +} + +pub struct MigrateToV5(sp_std::marker::PhantomData); +impl OnRuntimeUpgrade for MigrateToV5 { + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, &'static str> { + log::trace!(target: crate::configuration::LOG_TARGET, "Running pre_upgrade()"); + + ensure!(StorageVersion::get::>() == 4, "The migration requires version 4"); + Ok(Vec::new()) + } + + fn on_runtime_upgrade() -> Weight { + if StorageVersion::get::>() == 4 { + let weight_consumed = migrate_to_v5::(); + + log::info!(target: configuration::LOG_TARGET, "MigrateToV5 executed successfully"); + StorageVersion::new(5).put::>(); + + weight_consumed + } else { + log::warn!(target: configuration::LOG_TARGET, "MigrateToV5 should be removed."); + T::DbWeight::get().reads(1) + } + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_state: Vec) -> Result<(), &'static str> { + log::trace!(target: crate::configuration::LOG_TARGET, "Running post_upgrade()"); + ensure!( + StorageVersion::get::>() == 5, + "Storage version should be 5 after the migration" + ); + + Ok(()) + } +} + +fn migrate_to_v5() -> Weight { + // Unusual formatting is justified: + // - make it easier to verify that fields assign what they supposed to assign. + // - this code is transient and will be removed after all migrations are done. + // - this code is important enough to optimize for legibility sacrificing consistency. + #[rustfmt::skip] + let translate = + |pre: V4HostConfiguration>| -> + V5HostConfiguration> + { + V5HostConfiguration { +max_code_size : pre.max_code_size, +max_head_data_size : pre.max_head_data_size, +max_upward_queue_count : pre.max_upward_queue_count, +max_upward_queue_size : pre.max_upward_queue_size, +max_upward_message_size : pre.max_upward_message_size, +max_upward_message_num_per_candidate : pre.max_upward_message_num_per_candidate, +hrmp_max_message_num_per_candidate : pre.hrmp_max_message_num_per_candidate, +validation_upgrade_cooldown : pre.validation_upgrade_cooldown, +validation_upgrade_delay : pre.validation_upgrade_delay, +max_pov_size : pre.max_pov_size, +max_downward_message_size : pre.max_downward_message_size, +ump_service_total_weight : pre.ump_service_total_weight, +hrmp_max_parachain_outbound_channels : pre.hrmp_max_parachain_outbound_channels, +hrmp_max_parathread_outbound_channels : pre.hrmp_max_parathread_outbound_channels, +hrmp_sender_deposit : pre.hrmp_sender_deposit, +hrmp_recipient_deposit : pre.hrmp_recipient_deposit, +hrmp_channel_max_capacity : pre.hrmp_channel_max_capacity, +hrmp_channel_max_total_size : pre.hrmp_channel_max_total_size, +hrmp_max_parachain_inbound_channels : pre.hrmp_max_parachain_inbound_channels, +hrmp_max_parathread_inbound_channels : pre.hrmp_max_parathread_inbound_channels, +hrmp_channel_max_message_size : pre.hrmp_channel_max_message_size, +code_retention_period : pre.code_retention_period, +parathread_cores : pre.parathread_cores, +parathread_retries : pre.parathread_retries, +group_rotation_frequency : pre.group_rotation_frequency, +chain_availability_period : pre.chain_availability_period, +thread_availability_period : pre.thread_availability_period, +scheduling_lookahead : pre.scheduling_lookahead, +max_validators_per_core : pre.max_validators_per_core, +max_validators : pre.max_validators, +dispute_period : pre.dispute_period, +dispute_post_conclusion_acceptance_period: pre.dispute_post_conclusion_acceptance_period, +no_show_slots : pre.no_show_slots, +n_delay_tranches : pre.n_delay_tranches, +zeroth_delay_tranche_width : pre.zeroth_delay_tranche_width, +needed_approvals : pre.needed_approvals, +relay_vrf_modulo_samples : pre.relay_vrf_modulo_samples, +ump_max_individual_weight : pre.ump_max_individual_weight, +pvf_checking_enabled : pre.pvf_checking_enabled, +pvf_voting_ttl : pre.pvf_voting_ttl, +minimum_validation_upgrade_delay : pre.minimum_validation_upgrade_delay, + +// Default values are zeroes, thus it's ensured allowed ancestry never crosses the upgrade block. +async_backing_params : AsyncBackingParams { max_candidate_depth: 0, allowed_ancestry_len: 0 }, + +// Default executor parameters set is empty +executor_params : Default::default(), + } + }; + + let v4 = V4ActiveConfig::::get() + .defensive_proof("Could not decode old config") + .unwrap_or_default(); + let v5 = translate(v4); + V5ActiveConfig::::set(Some(v5)); + + let pending_v4 = V4PendingConfigs::::get() + .defensive_proof("Could not decode old pending") + .unwrap_or_default(); + let mut pending_v5 = Vec::new(); + + for (session, v4) in pending_v4.into_iter() { + let v5 = translate(v4); + pending_v5.push((session, v5)); + } + V5PendingConfigs::::set(Some(pending_v5.clone())); + + let num_configs = (pending_v5.len() + 1) as u64; + T::DbWeight::get().reads_writes(num_configs, num_configs) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mock::{new_test_ext, Test}; + use primitives::ExecutorParams; + + #[test] + fn v4_deserialized_from_actual_data() { + // Example how to get new `raw_config`: + // We'll obtain the raw_config at a specified a block + // Steps: + // 1. Go to Polkadot.js -> Developer -> Chain state -> Storage: https://polkadot.js.org/apps/#/chainstate + // 2. Set these parameters: + // 2.1. selected state query: configuration; activeConfig(): PolkadotRuntimeParachainsConfigurationHostConfiguration + // 2.2. blockhash to query at: 0xf89d3ab5312c5f70d396dc59612f0aa65806c798346f9db4b35278baed2e0e53 (the hash of the block) + // 2.3. Note the value of encoded storage key -> 0x06de3d8a54d27e44a9d5ce189618f22db4b49d95320d9021994c850f25b8e385 for the referenced block. + // 2.4. You'll also need the decoded values to update the test. + // 3. Go to Polkadot.js -> Developer -> Chain state -> Raw storage + // 3.1 Enter the encoded storage key and you get the raw config. + + // This exceeds the maximal line width length, but that's fine, since this is not code and + // doesn't need to be read and also leaving it as one line allows to easily copy it. + let raw_config = hex_literal::hex!["0000a000005000000a00000000c8000000c800000a0000000a000000100e0000580200000000500000c800000700e8764817020040011e00000000000000005039278c0400000000000000000000005039278c0400000000000000000000e8030000009001001e00000000000000009001008070000000000000000000000a0000000a0000000a00000001000000010500000001c80000000600000058020000580200000200000059000000000000001e000000280000000700c817a80402004001010200000014000000"]; + + let v4 = v5::V4HostConfiguration::::decode(&mut &raw_config[..]) + .unwrap(); + + // We check only a sample of the values here. If we missed any fields or messed up data types + // that would skew all the fields coming after. + assert_eq!(v4.max_code_size, 10_485_760); + assert_eq!(v4.validation_upgrade_cooldown, 3600); + assert_eq!(v4.max_pov_size, 5_242_880); + assert_eq!(v4.hrmp_channel_max_message_size, 102_400); + assert_eq!(v4.n_delay_tranches, 89); + assert_eq!(v4.ump_max_individual_weight, Weight::from_parts(20_000_000_000, 5_242_880)); + assert_eq!(v4.minimum_validation_upgrade_delay, 20); + } + + #[test] + fn test_migrate_to_v5() { + // Host configuration has lots of fields. However, in this migration we only remove one field. + // The most important part to check are a couple of the last fields. We also pick + // extra fields to check arbitrarily, e.g. depending on their position (i.e. the middle) and + // also their type. + // + // We specify only the picked fields and the rest should be provided by the `Default` + // implementation. That implementation is copied over between the two types and should work + // fine. + let v4 = v5::V4HostConfiguration:: { + ump_max_individual_weight: Weight::from_parts(0x71616e6f6e0au64, 0x71616e6f6e0au64), + needed_approvals: 69, + thread_availability_period: 55, + hrmp_recipient_deposit: 1337, + max_pov_size: 1111, + chain_availability_period: 33, + minimum_validation_upgrade_delay: 20, + ..Default::default() + }; + + let mut pending_configs = Vec::new(); + pending_configs.push((100, v4.clone())); + pending_configs.push((300, v4.clone())); + + new_test_ext(Default::default()).execute_with(|| { + // Implant the v4 version in the state. + V4ActiveConfig::::set(Some(v4)); + V4PendingConfigs::::set(Some(pending_configs)); + + migrate_to_v5::(); + + let v5 = V5ActiveConfig::::get().unwrap(); + let mut configs_to_check = V5PendingConfigs::::get().unwrap(); + configs_to_check.push((0, v5.clone())); + + for (_, v4) in configs_to_check { + #[rustfmt::skip] + { + assert_eq!(v4.max_code_size , v5.max_code_size); + assert_eq!(v4.max_head_data_size , v5.max_head_data_size); + assert_eq!(v4.max_upward_queue_count , v5.max_upward_queue_count); + assert_eq!(v4.max_upward_queue_size , v5.max_upward_queue_size); + assert_eq!(v4.max_upward_message_size , v5.max_upward_message_size); + assert_eq!(v4.max_upward_message_num_per_candidate , v5.max_upward_message_num_per_candidate); + assert_eq!(v4.hrmp_max_message_num_per_candidate , v5.hrmp_max_message_num_per_candidate); + assert_eq!(v4.validation_upgrade_cooldown , v5.validation_upgrade_cooldown); + assert_eq!(v4.validation_upgrade_delay , v5.validation_upgrade_delay); + assert_eq!(v4.max_pov_size , v5.max_pov_size); + assert_eq!(v4.max_downward_message_size , v5.max_downward_message_size); + assert_eq!(v4.ump_service_total_weight , v5.ump_service_total_weight); + assert_eq!(v4.hrmp_max_parachain_outbound_channels , v5.hrmp_max_parachain_outbound_channels); + assert_eq!(v4.hrmp_max_parathread_outbound_channels , v5.hrmp_max_parathread_outbound_channels); + assert_eq!(v4.hrmp_sender_deposit , v5.hrmp_sender_deposit); + assert_eq!(v4.hrmp_recipient_deposit , v5.hrmp_recipient_deposit); + assert_eq!(v4.hrmp_channel_max_capacity , v5.hrmp_channel_max_capacity); + assert_eq!(v4.hrmp_channel_max_total_size , v5.hrmp_channel_max_total_size); + assert_eq!(v4.hrmp_max_parachain_inbound_channels , v5.hrmp_max_parachain_inbound_channels); + assert_eq!(v4.hrmp_max_parathread_inbound_channels , v5.hrmp_max_parathread_inbound_channels); + assert_eq!(v4.hrmp_channel_max_message_size , v5.hrmp_channel_max_message_size); + assert_eq!(v4.code_retention_period , v5.code_retention_period); + assert_eq!(v4.parathread_cores , v5.parathread_cores); + assert_eq!(v4.parathread_retries , v5.parathread_retries); + assert_eq!(v4.group_rotation_frequency , v5.group_rotation_frequency); + assert_eq!(v4.chain_availability_period , v5.chain_availability_period); + assert_eq!(v4.thread_availability_period , v5.thread_availability_period); + assert_eq!(v4.scheduling_lookahead , v5.scheduling_lookahead); + assert_eq!(v4.max_validators_per_core , v5.max_validators_per_core); + assert_eq!(v4.max_validators , v5.max_validators); + assert_eq!(v4.dispute_period , v5.dispute_period); + assert_eq!(v4.no_show_slots , v5.no_show_slots); + assert_eq!(v4.n_delay_tranches , v5.n_delay_tranches); + assert_eq!(v4.zeroth_delay_tranche_width , v5.zeroth_delay_tranche_width); + assert_eq!(v4.needed_approvals , v5.needed_approvals); + assert_eq!(v4.relay_vrf_modulo_samples , v5.relay_vrf_modulo_samples); + assert_eq!(v4.ump_max_individual_weight , v5.ump_max_individual_weight); + assert_eq!(v4.pvf_checking_enabled , v5.pvf_checking_enabled); + assert_eq!(v4.pvf_voting_ttl , v5.pvf_voting_ttl); + assert_eq!(v4.minimum_validation_upgrade_delay , v5.minimum_validation_upgrade_delay); + }; // ; makes this a statement. `rustfmt::skip` cannot be put on an expression. + + // additional checks for async backing. + assert_eq!(v5.async_backing_params.allowed_ancestry_len, 0); + assert_eq!(v5.async_backing_params.max_candidate_depth, 0); + assert_eq!(v5.executor_params, ExecutorParams::new()); + } + }); + } +} diff --git a/runtime/parachains/src/configuration/migration/v6.rs b/runtime/parachains/src/configuration/migration/v6.rs new file mode 100644 index 000000000000..44acaee71f49 --- /dev/null +++ b/runtime/parachains/src/configuration/migration/v6.rs @@ -0,0 +1,292 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! A module that is responsible for migration of storage. + +use crate::configuration::{self, Config, Pallet}; +use frame_support::{ + pallet_prelude::*, + traits::{Defensive, StorageVersion}, + weights::Weight, +}; +use frame_system::pallet_prelude::BlockNumberFor; +use sp_std::vec::Vec; + +use frame_support::traits::OnRuntimeUpgrade; +use primitives::SessionIndex; +#[cfg(feature = "try-runtime")] +use sp_std::prelude::*; + +use super::v5::V5HostConfiguration; +// Change this once there is V7. +type V6HostConfiguration = configuration::HostConfiguration; + +#[frame_support::storage_alias] +pub(crate) type V5ActiveConfig = + StorageValue, V5HostConfiguration>, OptionQuery>; + +#[frame_support::storage_alias] +pub(crate) type V5PendingConfigs = StorageValue< + Pallet, + Vec<(SessionIndex, V5HostConfiguration>)>, + OptionQuery, +>; + +#[frame_support::storage_alias] +pub(crate) type V6ActiveConfig = + StorageValue, V6HostConfiguration>, OptionQuery>; + +#[frame_support::storage_alias] +pub(crate) type V6PendingConfigs = StorageValue< + Pallet, + Vec<(SessionIndex, V6HostConfiguration>)>, + OptionQuery, +>; + +pub struct MigrateToV6(sp_std::marker::PhantomData); +impl OnRuntimeUpgrade for MigrateToV6 { + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, &'static str> { + log::trace!(target: crate::configuration::LOG_TARGET, "Running pre_upgrade()"); + + ensure!(StorageVersion::get::>() == 5, "The migration requires version 4"); + Ok(Vec::new()) + } + + fn on_runtime_upgrade() -> Weight { + if StorageVersion::get::>() == 5 { + let weight_consumed = migrate_to_v6::(); + + log::info!(target: configuration::LOG_TARGET, "MigrateToV6 executed successfully"); + StorageVersion::new(6).put::>(); + + weight_consumed + } else { + log::warn!(target: configuration::LOG_TARGET, "MigrateToV6 should be removed."); + T::DbWeight::get().reads(1) + } + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_state: Vec) -> Result<(), &'static str> { + log::trace!(target: crate::configuration::LOG_TARGET, "Running post_upgrade()"); + ensure!( + StorageVersion::get::>() == 6, + "Storage version should be 6 after the migration" + ); + + Ok(()) + } +} + +fn migrate_to_v6() -> Weight { + // Unusual formatting is justified: + // - make it easier to verify that fields assign what they supposed to assign. + // - this code is transient and will be removed after all migrations are done. + // - this code is important enough to optimize for legibility sacrificing consistency. + #[rustfmt::skip] + let translate = + |pre: V5HostConfiguration>| -> + V6HostConfiguration> + { + V6HostConfiguration { +max_code_size : pre.max_code_size, +max_head_data_size : pre.max_head_data_size, +max_upward_queue_count : pre.max_upward_queue_count, +max_upward_queue_size : pre.max_upward_queue_size, +max_upward_message_size : pre.max_upward_message_size, +max_upward_message_num_per_candidate : pre.max_upward_message_num_per_candidate, +hrmp_max_message_num_per_candidate : pre.hrmp_max_message_num_per_candidate, +validation_upgrade_cooldown : pre.validation_upgrade_cooldown, +validation_upgrade_delay : pre.validation_upgrade_delay, +max_pov_size : pre.max_pov_size, +max_downward_message_size : pre.max_downward_message_size, +hrmp_max_parachain_outbound_channels : pre.hrmp_max_parachain_outbound_channels, +hrmp_max_parathread_outbound_channels : pre.hrmp_max_parathread_outbound_channels, +hrmp_sender_deposit : pre.hrmp_sender_deposit, +hrmp_recipient_deposit : pre.hrmp_recipient_deposit, +hrmp_channel_max_capacity : pre.hrmp_channel_max_capacity, +hrmp_channel_max_total_size : pre.hrmp_channel_max_total_size, +hrmp_max_parachain_inbound_channels : pre.hrmp_max_parachain_inbound_channels, +hrmp_max_parathread_inbound_channels : pre.hrmp_max_parathread_inbound_channels, +hrmp_channel_max_message_size : pre.hrmp_channel_max_message_size, +code_retention_period : pre.code_retention_period, +parathread_cores : pre.parathread_cores, +parathread_retries : pre.parathread_retries, +group_rotation_frequency : pre.group_rotation_frequency, +chain_availability_period : pre.chain_availability_period, +thread_availability_period : pre.thread_availability_period, +scheduling_lookahead : pre.scheduling_lookahead, +max_validators_per_core : pre.max_validators_per_core, +max_validators : pre.max_validators, +dispute_period : pre.dispute_period, +dispute_post_conclusion_acceptance_period: pre.dispute_post_conclusion_acceptance_period, +no_show_slots : pre.no_show_slots, +n_delay_tranches : pre.n_delay_tranches, +zeroth_delay_tranche_width : pre.zeroth_delay_tranche_width, +needed_approvals : pre.needed_approvals, +relay_vrf_modulo_samples : pre.relay_vrf_modulo_samples, +pvf_checking_enabled : pre.pvf_checking_enabled, +pvf_voting_ttl : pre.pvf_voting_ttl, +minimum_validation_upgrade_delay : pre.minimum_validation_upgrade_delay, +async_backing_params : pre.async_backing_params, +executor_params : pre.executor_params, + } + }; + + let v5 = V5ActiveConfig::::get() + .defensive_proof("Could not decode old config") + .unwrap_or_default(); + let v6 = translate(v5); + V6ActiveConfig::::set(Some(v6)); + + let pending_v4 = V5PendingConfigs::::get() + .defensive_proof("Could not decode old pending") + .unwrap_or_default(); + let mut pending_v5 = Vec::new(); + + for (session, v5) in pending_v4.into_iter() { + let v6 = translate(v5); + pending_v5.push((session, v6)); + } + V6PendingConfigs::::set(Some(pending_v5.clone())); + + let num_configs = (pending_v5.len() + 1) as u64; + T::DbWeight::get().reads_writes(num_configs, num_configs) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mock::{new_test_ext, Test}; + + #[test] + fn v5_deserialized_from_actual_data() { + // Example how to get new `raw_config`: + // We'll obtain the raw_config at a specified a block + // Steps: + // 1. Go to Polkadot.js -> Developer -> Chain state -> Storage: https://polkadot.js.org/apps/#/chainstate + // 2. Set these parameters: + // 2.1. selected state query: configuration; activeConfig(): PolkadotRuntimeParachainsConfigurationHostConfiguration + // 2.2. blockhash to query at: 0xf89d3ab5312c5f70d396dc59612f0aa65806c798346f9db4b35278baed2e0e53 (the hash of the block) + // 2.3. Note the value of encoded storage key -> 0x06de3d8a54d27e44a9d5ce189618f22db4b49d95320d9021994c850f25b8e385 for the referenced block. + // 2.4. You'll also need the decoded values to update the test. + // 3. Go to Polkadot.js -> Developer -> Chain state -> Raw storage + // 3.1 Enter the encoded storage key and you get the raw config. + + // This exceeds the maximal line width length, but that's fine, since this is not code and + // doesn't need to be read and also leaving it as one line allows to easily copy it. + let raw_config = hex_literal::hex!["00005000005000000a00000000c8000000c800000a0000000a000000c80000006400000000000000000000000000500000c800000700e8764817020040010a0000000000000000c0220fca950300000000000000000000c0220fca9503000000000000000000e8030000009001000a0000000000000000900100008070000000000000000000000a000000050000000500000001000000010500000001c8000000060000005802000002000000280000000000000002000000010000000700c817a8040200400101020000000f000000"]; + + let v5 = + V5HostConfiguration::::decode(&mut &raw_config[..]).unwrap(); + + // We check only a sample of the values here. If we missed any fields or messed up data types + // that would skew all the fields coming after. + assert_eq!(v5.max_code_size, 5242880); + assert_eq!(v5.validation_upgrade_cooldown, 200); + assert_eq!(v5.max_pov_size, 5_242_880); + assert_eq!(v5.hrmp_channel_max_message_size, 102_400); + assert_eq!(v5.n_delay_tranches, 40); + assert_eq!(v5.ump_max_individual_weight, Weight::from_parts(20_000_000_000, 5_242_880)); + assert_eq!(v5.minimum_validation_upgrade_delay, 15); // This is the last field in the struct. + } + + #[test] + fn test_migrate_to_v6() { + // Host configuration has lots of fields. However, in this migration we only remove one field. + // The most important part to check are a couple of the last fields. We also pick + // extra fields to check arbitrarily, e.g. depending on their position (i.e. the middle) and + // also their type. + // + // We specify only the picked fields and the rest should be provided by the `Default` + // implementation. That implementation is copied over between the two types and should work + // fine. + let v5 = V5HostConfiguration:: { + ump_max_individual_weight: Weight::from_parts(0x71616e6f6e0au64, 0x71616e6f6e0au64), + needed_approvals: 69, + thread_availability_period: 55, + hrmp_recipient_deposit: 1337, + max_pov_size: 1111, + chain_availability_period: 33, + minimum_validation_upgrade_delay: 20, + ..Default::default() + }; + + let mut pending_configs = Vec::new(); + pending_configs.push((100, v5.clone())); + pending_configs.push((300, v5.clone())); + + new_test_ext(Default::default()).execute_with(|| { + // Implant the v5 version in the state. + V5ActiveConfig::::set(Some(v5)); + V5PendingConfigs::::set(Some(pending_configs)); + + migrate_to_v6::(); + + let v6 = V6ActiveConfig::::get().unwrap(); + let mut configs_to_check = V6PendingConfigs::::get().unwrap(); + configs_to_check.push((0, v6.clone())); + + for (_, v5) in configs_to_check { + #[rustfmt::skip] + { + assert_eq!(v5.max_code_size , v6.max_code_size); + assert_eq!(v5.max_head_data_size , v6.max_head_data_size); + assert_eq!(v5.max_upward_queue_count , v6.max_upward_queue_count); + assert_eq!(v5.max_upward_queue_size , v6.max_upward_queue_size); + assert_eq!(v5.max_upward_message_size , v6.max_upward_message_size); + assert_eq!(v5.max_upward_message_num_per_candidate , v6.max_upward_message_num_per_candidate); + assert_eq!(v5.hrmp_max_message_num_per_candidate , v6.hrmp_max_message_num_per_candidate); + assert_eq!(v5.validation_upgrade_cooldown , v6.validation_upgrade_cooldown); + assert_eq!(v5.validation_upgrade_delay , v6.validation_upgrade_delay); + assert_eq!(v5.max_pov_size , v6.max_pov_size); + assert_eq!(v5.max_downward_message_size , v6.max_downward_message_size); + assert_eq!(v5.hrmp_max_parachain_outbound_channels , v6.hrmp_max_parachain_outbound_channels); + assert_eq!(v5.hrmp_max_parathread_outbound_channels , v6.hrmp_max_parathread_outbound_channels); + assert_eq!(v5.hrmp_sender_deposit , v6.hrmp_sender_deposit); + assert_eq!(v5.hrmp_recipient_deposit , v6.hrmp_recipient_deposit); + assert_eq!(v5.hrmp_channel_max_capacity , v6.hrmp_channel_max_capacity); + assert_eq!(v5.hrmp_channel_max_total_size , v6.hrmp_channel_max_total_size); + assert_eq!(v5.hrmp_max_parachain_inbound_channels , v6.hrmp_max_parachain_inbound_channels); + assert_eq!(v5.hrmp_max_parathread_inbound_channels , v6.hrmp_max_parathread_inbound_channels); + assert_eq!(v5.hrmp_channel_max_message_size , v6.hrmp_channel_max_message_size); + assert_eq!(v5.code_retention_period , v6.code_retention_period); + assert_eq!(v5.parathread_cores , v6.parathread_cores); + assert_eq!(v5.parathread_retries , v6.parathread_retries); + assert_eq!(v5.group_rotation_frequency , v6.group_rotation_frequency); + assert_eq!(v5.chain_availability_period , v6.chain_availability_period); + assert_eq!(v5.thread_availability_period , v6.thread_availability_period); + assert_eq!(v5.scheduling_lookahead , v6.scheduling_lookahead); + assert_eq!(v5.max_validators_per_core , v6.max_validators_per_core); + assert_eq!(v5.max_validators , v6.max_validators); + assert_eq!(v5.dispute_period , v6.dispute_period); + assert_eq!(v5.no_show_slots , v6.no_show_slots); + assert_eq!(v5.n_delay_tranches , v6.n_delay_tranches); + assert_eq!(v5.zeroth_delay_tranche_width , v6.zeroth_delay_tranche_width); + assert_eq!(v5.needed_approvals , v6.needed_approvals); + assert_eq!(v5.relay_vrf_modulo_samples , v6.relay_vrf_modulo_samples); + assert_eq!(v5.pvf_checking_enabled , v6.pvf_checking_enabled); + assert_eq!(v5.pvf_voting_ttl , v6.pvf_voting_ttl); + assert_eq!(v5.minimum_validation_upgrade_delay , v6.minimum_validation_upgrade_delay); + assert_eq!(v5.async_backing_params.allowed_ancestry_len, v6.async_backing_params.allowed_ancestry_len); + assert_eq!(v5.async_backing_params.max_candidate_depth , v6.async_backing_params.max_candidate_depth); + assert_eq!(v5.executor_params , v6.executor_params); + }; // ; makes this a statement. `rustfmt::skip` cannot be put on an expression. + } + }); + } +} diff --git a/runtime/parachains/src/configuration/migration_ump.rs b/runtime/parachains/src/configuration/migration_ump.rs new file mode 100644 index 000000000000..ef113aac01f4 --- /dev/null +++ b/runtime/parachains/src/configuration/migration_ump.rs @@ -0,0 +1,111 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! A module that is responsible for migration of UMP storage. + +#![allow(unused_imports)] // Since we use features. + +use crate::configuration::{self, ActiveConfig, Config, PendingConfigs, WeightInfo, LOG_TARGET}; +use parity_scale_codec::{Decode, Encode}; + +pub mod latest { + use super::*; + use frame_support::{pallet_prelude::Weight, traits::OnRuntimeUpgrade}; + + /// Force update the UMP limits in the parachain host config. + // NOTE: `OnRuntimeUpgrade` does not have a `self`, so everything must be compile time. + pub struct ScheduleConfigUpdate< + T, + const MAX_UPWARD_QUEUE_SIZE: u32, + const MAX_UPWARD_QUEUE_COUNT: u32, + const MAX_UPWARD_MESSAGE_SIZE: u32, + const MAX_UPWARD_MESSAGE_NUM_PER_CANDIDATE: u32, + >(core::marker::PhantomData); + + impl< + T: Config, + const MAX_UPWARD_QUEUE_SIZE: u32, + const MAX_UPWARD_QUEUE_COUNT: u32, + const MAX_UPWARD_MESSAGE_SIZE: u32, + const MAX_UPWARD_MESSAGE_NUM_PER_CANDIDATE: u32, + > OnRuntimeUpgrade + for ScheduleConfigUpdate< + T, + MAX_UPWARD_QUEUE_SIZE, + MAX_UPWARD_QUEUE_COUNT, + MAX_UPWARD_MESSAGE_SIZE, + MAX_UPWARD_MESSAGE_NUM_PER_CANDIDATE, + > + { + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, &'static str> { + log::info!(target: LOG_TARGET, "pre_upgrade"); + let mut pending = PendingConfigs::::get(); + pending.sort_by_key(|(s, _)| *s); + + log::info!( + target: LOG_TARGET, + "Active HostConfig:\n\n{:#?}\n", + ActiveConfig::::get() + ); + log::info!( + target: LOG_TARGET, + "Last pending HostConfig upgrade:\n\n{:#?}\n", + pending.last() + ); + + Ok((pending.len() as u32).encode()) + } + + fn on_runtime_upgrade() -> Weight { + if let Err(e) = configuration::Pallet::::schedule_config_update(|cfg| { + cfg.max_upward_queue_size = MAX_UPWARD_QUEUE_SIZE; + cfg.max_upward_queue_count = MAX_UPWARD_QUEUE_COUNT; + cfg.max_upward_message_size = MAX_UPWARD_MESSAGE_SIZE; + cfg.max_upward_message_num_per_candidate = MAX_UPWARD_MESSAGE_NUM_PER_CANDIDATE; + }) { + log::error!( + target: LOG_TARGET, + "\n!!!!!!!!!!!!!!!!!!!!!!!!\nFailed to schedule HostConfig upgrade: {:?}\n!!!!!!!!!!!!!!!!!!!!!!!!\n", + e, + ); + } + + T::WeightInfo::set_config_with_block_number() + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: sp_std::vec::Vec) -> Result<(), &'static str> { + log::info!(target: LOG_TARGET, "post_upgrade"); + let old_pending: u32 = Decode::decode(&mut &state[..]).expect("Known good"); + let mut pending = PendingConfigs::::get(); + pending.sort_by_key(|(s, _)| *s); + + log::info!( + target: LOG_TARGET, + "Last pending HostConfig upgrade:\n\n{:#?}\n", + pending.last() + ); + assert_eq!( + pending.len(), + old_pending as usize + 1, + "There must be a new pending upgrade enqueued" + ); + + Ok(()) + } + } +} diff --git a/runtime/parachains/src/configuration/tests.rs b/runtime/parachains/src/configuration/tests.rs index d076865bf6bb..d36394325bd6 100644 --- a/runtime/parachains/src/configuration/tests.rs +++ b/runtime/parachains/src/configuration/tests.rs @@ -16,7 +16,7 @@ use super::*; use crate::mock::{new_test_ext, Configuration, ParasShared, RuntimeOrigin, Test}; -use frame_support::{assert_err, assert_ok}; +use frame_support::{assert_err, assert_noop, assert_ok}; fn on_new_session(session_index: SessionIndex) -> (HostConfiguration, HostConfiguration) { ParasShared::set_session_index(session_index); @@ -309,7 +309,6 @@ fn setting_pending_config_members() { max_upward_queue_count: 1337, max_upward_queue_size: 228, max_downward_message_size: 2048, - ump_service_total_weight: Weight::from_parts(20000, 20000), max_upward_message_size: 448, max_upward_message_num_per_candidate: 5, hrmp_sender_deposit: 22, @@ -322,7 +321,6 @@ fn setting_pending_config_members() { hrmp_max_parachain_outbound_channels: 10, hrmp_max_parathread_outbound_channels: 20, hrmp_max_message_num_per_candidate: 20, - ump_max_individual_weight: Weight::from_parts(909, 909), pvf_checking_enabled: true, pvf_voting_ttl: 3, minimum_validation_upgrade_delay: 20, @@ -418,16 +416,18 @@ fn setting_pending_config_members() { new_config.max_upward_queue_size, ) .unwrap(); + assert_noop!( + Configuration::set_max_upward_queue_size( + RuntimeOrigin::root(), + MAX_UPWARD_MESSAGE_SIZE_BOUND + 1, + ), + Error::::InvalidNewValue + ); Configuration::set_max_downward_message_size( RuntimeOrigin::root(), new_config.max_downward_message_size, ) .unwrap(); - Configuration::set_ump_service_total_weight( - RuntimeOrigin::root(), - new_config.ump_service_total_weight, - ) - .unwrap(); Configuration::set_max_upward_message_size( RuntimeOrigin::root(), new_config.max_upward_message_size, @@ -488,11 +488,6 @@ fn setting_pending_config_members() { new_config.hrmp_max_message_num_per_candidate, ) .unwrap(); - Configuration::set_ump_max_individual_weight( - RuntimeOrigin::root(), - new_config.ump_max_individual_weight, - ) - .unwrap(); Configuration::set_pvf_checking_enabled( RuntimeOrigin::root(), new_config.pvf_checking_enabled, diff --git a/runtime/parachains/src/hrmp/tests.rs b/runtime/parachains/src/hrmp/tests.rs index 7bffdc818f7c..709d56109b78 100644 --- a/runtime/parachains/src/hrmp/tests.rs +++ b/runtime/parachains/src/hrmp/tests.rs @@ -15,18 +15,16 @@ // along with Polkadot. If not, see . use super::*; -use crate::{ - mock::{ - new_test_ext, Configuration, Hrmp, MockGenesisConfig, Paras, ParasShared, - RuntimeEvent as MockEvent, RuntimeOrigin, System, Test, - }, - paras::ParaKind, +use crate::mock::{ + deregister_parachain, new_test_ext, register_parachain, register_parachain_with_balance, + Configuration, Hrmp, MockGenesisConfig, Paras, ParasShared, RuntimeEvent as MockEvent, + RuntimeOrigin, System, Test, }; -use frame_support::{assert_noop, assert_ok, traits::Currency as _}; -use primitives::{BlockNumber, ValidationCode}; +use frame_support::assert_noop; +use primitives::BlockNumber; use std::collections::BTreeMap; -fn run_to_block(to: BlockNumber, new_session: Option>) { +pub(crate) fn run_to_block(to: BlockNumber, new_session: Option>) { let config = Configuration::config(); while System::block_number() < to { let b = System::block_number(); @@ -129,29 +127,6 @@ fn default_genesis_config() -> MockGenesisConfig { } } -fn register_parachain_with_balance(id: ParaId, balance: Balance) { - let validation_code: ValidationCode = vec![1].into(); - assert_ok!(Paras::schedule_para_initialize( - id, - crate::paras::ParaGenesisArgs { - para_kind: ParaKind::Parachain, - genesis_head: vec![1].into(), - validation_code: validation_code.clone(), - }, - )); - - assert_ok!(Paras::add_trusted_validation_code(RuntimeOrigin::root(), validation_code)); - ::Currency::make_free_balance_be(&id.into_account_truncating(), balance); -} - -fn register_parachain(id: ParaId) { - register_parachain_with_balance(id, 1000); -} - -fn deregister_parachain(id: ParaId) { - assert_ok!(Paras::schedule_para_cleanup(id)); -} - fn channel_exists(sender: ParaId, recipient: ParaId) -> bool { HrmpChannels::::get(&HrmpChannelId { sender, recipient }).is_some() } diff --git a/runtime/parachains/src/inclusion/benchmarking.rs b/runtime/parachains/src/inclusion/benchmarking.rs new file mode 100644 index 000000000000..0471c0c3b2bf --- /dev/null +++ b/runtime/parachains/src/inclusion/benchmarking.rs @@ -0,0 +1,41 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +use super::*; +use frame_benchmarking::benchmarks; +use pallet_message_queue as mq; + +benchmarks! { + where_clause { + where + T: mq::Config, + } + + receive_upward_messages { + let i in 1 .. 1000; + + let max_len = mq::MaxMessageLenOf::::get() as usize; + let para = 42u32.into(); // not especially important. + let upward_messages = vec![vec![0; max_len]; i as usize]; + Pallet::::receive_upward_messages(para, vec![vec![0; max_len]; 1].as_slice()); + }: { Pallet::::receive_upward_messages(para, upward_messages.as_slice()) } + + impl_benchmark_test_suite!( + Pallet, + crate::mock::new_test_ext(Default::default()), + crate::mock::Test + ); +} diff --git a/runtime/parachains/src/inclusion/mod.rs b/runtime/parachains/src/inclusion/mod.rs index 7dc15ec504b0..6af59090a073 100644 --- a/runtime/parachains/src/inclusion/mod.rs +++ b/runtime/parachains/src/inclusion/mod.rs @@ -21,20 +21,32 @@ //! to included. use crate::{ - configuration, disputes, dmp, hrmp, paras, paras_inherent::DisputedBitfield, - scheduler::CoreAssignment, shared, ump, + configuration::{self, HostConfiguration}, + disputes, dmp, hrmp, paras, + paras_inherent::DisputedBitfield, + scheduler::CoreAssignment, + shared, }; use bitvec::{order::Lsb0 as BitOrderLsb0, vec::BitVec}; -use frame_support::pallet_prelude::*; +use frame_support::{ + defensive, + pallet_prelude::*, + traits::{Defensive, EnqueueMessage}, + BoundedSlice, +}; +use pallet_message_queue::OnQueueChanged; use parity_scale_codec::{Decode, Encode}; use primitives::{ - supermajority_threshold, AvailabilityBitfield, BackedCandidate, CandidateCommitments, - CandidateDescriptor, CandidateHash, CandidateReceipt, CommittedCandidateReceipt, CoreIndex, - GroupIndex, Hash, HeadData, Id as ParaId, SigningContext, UncheckedSignedAvailabilityBitfields, - ValidatorId, ValidatorIndex, ValidityAttestation, + supermajority_threshold, well_known_keys, AvailabilityBitfield, BackedCandidate, + CandidateCommitments, CandidateDescriptor, CandidateHash, CandidateReceipt, + CommittedCandidateReceipt, CoreIndex, GroupIndex, Hash, HeadData, Id as ParaId, SigningContext, + UncheckedSignedAvailabilityBitfields, UpwardMessage, ValidatorId, ValidatorIndex, + ValidityAttestation, }; use scale_info::TypeInfo; -use sp_runtime::{traits::One, DispatchError}; +use sp_runtime::{traits::One, DispatchError, SaturatedConversion, Saturating}; +#[cfg(feature = "std")] +use sp_std::fmt; use sp_std::{collections::btree_set::BTreeSet, prelude::*}; pub use pallet::*; @@ -42,6 +54,32 @@ pub use pallet::*; #[cfg(test)] pub(crate) mod tests; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; + +pub trait WeightInfo { + fn receive_upward_messages(i: u32) -> Weight; +} + +pub struct TestWeightInfo; +impl WeightInfo for TestWeightInfo { + fn receive_upward_messages(_: u32) -> Weight { + Weight::MAX + } +} + +impl WeightInfo for () { + fn receive_upward_messages(_: u32) -> Weight { + Weight::zero() + } +} + +/// Maximum value that `config.max_upward_message_size` can be set to. +/// +/// This is used for benchmarking sanely bounding relevant storage items. It is expected from the `configuration` +/// pallet to check these values before setting. +pub const MAX_UPWARD_MESSAGE_SIZE_BOUND: u32 = 128 * 1024; + /// A bitfield signed by a validator indicating that it is keeping its piece of the erasure-coding /// for any backed candidates referred to by a `1` bit available. /// @@ -179,6 +217,55 @@ pub fn minimum_backing_votes(n_validators: usize) -> usize { sp_std::cmp::min(n_validators, 2) } +/// Reads the footprint of queues for a specific origin type. +pub trait QueueFootprinter { + type Origin; + + fn message_count(origin: Self::Origin) -> u64; +} + +impl QueueFootprinter for () { + type Origin = UmpQueueId; + + fn message_count(_: Self::Origin) -> u64 { + 0 + } +} + +/// Aggregate message origin for the `MessageQueue` pallet. +/// +/// Can be extended to serve further use-cases besides just UMP. Is stored in storage, so any change +/// to existing values will require a migration. +#[derive(Encode, Decode, Clone, MaxEncodedLen, Eq, PartialEq, RuntimeDebug, TypeInfo)] +pub enum AggregateMessageOrigin { + /// Inbound upward message. + #[codec(index = 0)] + Ump(UmpQueueId), +} + +/// Identifies a UMP queue inside the `MessageQueue` pallet. +/// +/// It is written in verbose form since future variants like `Loopback` and `Bridged` are already +/// forseeable. +#[derive(Encode, Decode, Clone, MaxEncodedLen, Eq, PartialEq, RuntimeDebug, TypeInfo)] +pub enum UmpQueueId { + /// The message originated from this parachain. + #[codec(index = 0)] + Para(ParaId), +} + +#[cfg(feature = "runtime-benchmarks")] +impl From for AggregateMessageOrigin { + fn from(n: u32) -> Self { + // Some dummy for the benchmarks. + Self::Ump(UmpQueueId::Para(n.into())) + } +} + +/// The maximal length of a UMP message. +pub type MaxUmpMessageLenOf = + <::MessageQueue as EnqueueMessage>::MaxMessageLen; + #[frame_support::pallet] pub mod pallet { use super::*; @@ -193,13 +280,22 @@ pub mod pallet { + shared::Config + paras::Config + dmp::Config - + ump::Config + hrmp::Config + configuration::Config { type RuntimeEvent: From> + IsType<::RuntimeEvent>; type DisputesHandler: disputes::DisputesHandler; type RewardValidators: RewardValidators; + + /// The system message queue. + /// + /// The message queue provides general queueing and processing functionality. Currently it + /// replaces the old `UMP` dispatch queue. Other use-cases can be implemented as well by + /// adding new variants to `AggregateMessageOrigin`. + type MessageQueue: EnqueueMessage; + + /// Weight info for the calls of this pallet. + type WeightInfo: WeightInfo; } #[pallet::event] @@ -211,6 +307,8 @@ pub mod pallet { CandidateIncluded(CandidateReceipt, HeadData, CoreIndex, GroupIndex), /// A candidate timed out. `[candidate, head_data]` CandidateTimedOut(CandidateReceipt, HeadData, CoreIndex), + /// Some upward messages have been received and will be processed. + UpwardMessagesReceived { from: ParaId, count: u32 }, } #[pallet::error] @@ -299,6 +397,71 @@ pub mod pallet { const LOG_TARGET: &str = "runtime::inclusion"; +/// The reason that a candidate's outputs were rejected for. +#[derive(derive_more::From)] +#[cfg_attr(feature = "std", derive(Debug))] +enum AcceptanceCheckErr { + HeadDataTooLarge, + /// Code upgrades are not permitted at the current time. + PrematureCodeUpgrade, + /// The new runtime blob is too large. + NewCodeTooLarge, + /// The candidate violated this DMP acceptance criteria. + ProcessedDownwardMessages(dmp::ProcessedDownwardMessagesAcceptanceErr), + /// The candidate violated this UMP acceptance criteria. + UpwardMessages(UmpAcceptanceCheckErr), + /// The candidate violated this HRMP watermark acceptance criteria. + HrmpWatermark(hrmp::HrmpWatermarkAcceptanceErr), + /// The candidate violated this outbound HRMP acceptance criteria. + OutboundHrmp(hrmp::OutboundHrmpAcceptanceErr), +} + +/// An error returned by [`check_upward_messages`] that indicates a violation of one of acceptance +/// criteria rules. +#[cfg_attr(test, derive(PartialEq))] +pub enum UmpAcceptanceCheckErr { + /// The maximal number of messages that can be submitted in one batch was exceeded. + MoreMessagesThanPermitted { sent: u32, permitted: u32 }, + /// The maximal size of a single message was exceeded. + MessageSize { idx: u32, msg_size: u32, max_size: u32 }, + /// The allowed number of messages in the queue was exceeded. + CapacityExceeded { count: u64, limit: u64 }, + /// The allowed combined message size in the queue was exceeded. + TotalSizeExceeded { total_size: u64, limit: u64 }, + /// A para-chain cannot send UMP messages while it is offboarding. + IsOffboarding, +} + +#[cfg(feature = "std")] +impl fmt::Debug for UmpAcceptanceCheckErr { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match *self { + UmpAcceptanceCheckErr::MoreMessagesThanPermitted { sent, permitted } => write!( + fmt, + "more upward messages than permitted by config ({} > {})", + sent, permitted, + ), + UmpAcceptanceCheckErr::MessageSize { idx, msg_size, max_size } => write!( + fmt, + "upward message idx {} larger than permitted by config ({} > {})", + idx, msg_size, max_size, + ), + UmpAcceptanceCheckErr::CapacityExceeded { count, limit } => write!( + fmt, + "the ump queue would have more items than permitted by config ({} > {})", + count, limit, + ), + UmpAcceptanceCheckErr::TotalSizeExceeded { total_size, limit } => write!( + fmt, + "the ump queue would have grown past the max size permitted by config ({} > {})", + total_size, limit, + ), + UmpAcceptanceCheckErr::IsOffboarding => + write!(fmt, "upward message rejected because the para is off-boarding",), + } + } +} + impl Pallet { /// Block initialization logic, called by initializer. pub(crate) fn initializer_initialize(_now: T::BlockNumber) -> Weight { @@ -311,12 +474,25 @@ impl Pallet { /// Handle an incoming session change. pub(crate) fn initializer_on_new_session( _notification: &crate::initializer::SessionChangeNotification, + outgoing_paras: &[ParaId], ) { // unlike most drain methods, drained elements are not cleared on `Drop` of the iterator // and require consumption. for _ in >::drain() {} for _ in >::drain() {} for _ in >::drain() {} + + Self::cleanup_outgoing_ump_dispatch_queues(outgoing_paras); + } + + pub(crate) fn cleanup_outgoing_ump_dispatch_queues(outgoing: &[ParaId]) { + for outgoing_para in outgoing { + Self::cleanup_outgoing_ump_dispatch_queue(*outgoing_para); + } + } + + pub(crate) fn cleanup_outgoing_ump_dispatch_queue(para: ParaId) { + T::MessageQueue::sweep_queue(AggregateMessageOrigin::Ump(UmpQueueId::Para(para))); } /// Extract the freed cores based on cores that became available. @@ -698,20 +874,22 @@ impl Pallet { let relay_parent_number = now; let check_ctx = CandidateCheckContext::::new(now, relay_parent_number); - if let Err(err) = check_ctx.check_validation_outputs( - para_id, - &validation_outputs.head_data, - &validation_outputs.new_validation_code, - validation_outputs.processed_downward_messages, - &validation_outputs.upward_messages, - T::BlockNumber::from(validation_outputs.hrmp_watermark), - &validation_outputs.horizontal_messages, - ) { + if check_ctx + .check_validation_outputs( + para_id, + &validation_outputs.head_data, + &validation_outputs.new_validation_code, + validation_outputs.processed_downward_messages, + &validation_outputs.upward_messages, + T::BlockNumber::from(validation_outputs.hrmp_watermark), + &validation_outputs.horizontal_messages, + ) + .is_err() + { log::debug!( target: LOG_TARGET, - "Validation outputs checking for parachain `{}` failed: {:?}", + "Validation outputs checking for parachain `{}` failed", u32::from(para_id), - err, ); false } else { @@ -750,31 +928,31 @@ impl Pallet { // initial weight is config read. let mut weight = T::DbWeight::get().reads_writes(1, 0); if let Some(new_code) = commitments.new_validation_code { - weight += >::schedule_code_upgrade( + weight.saturating_add(>::schedule_code_upgrade( receipt.descriptor.para_id, new_code, relay_parent_number, &config, - ); + )); } // enact the messaging facet of the candidate. - weight += >::prune_dmq( + weight.saturating_accrue(>::prune_dmq( receipt.descriptor.para_id, commitments.processed_downward_messages, - ); - weight += >::receive_upward_messages( + )); + weight.saturating_accrue(Self::receive_upward_messages( receipt.descriptor.para_id, - commitments.upward_messages, - ); - weight += >::prune_hrmp( + commitments.upward_messages.as_slice(), + )); + weight.saturating_accrue(>::prune_hrmp( receipt.descriptor.para_id, T::BlockNumber::from(commitments.hrmp_watermark), - ); - weight += >::queue_outbound_hrmp( + )); + weight.saturating_accrue(>::queue_outbound_hrmp( receipt.descriptor.para_id, commitments.horizontal_messages, - ); + )); Self::deposit_event(Event::::CandidateIncluded( plain, @@ -783,12 +961,105 @@ impl Pallet { backing_group, )); - weight + - >::note_new_head( - receipt.descriptor.para_id, - commitments.head_data, - relay_parent_number, - ) + weight.saturating_add(>::note_new_head( + receipt.descriptor.para_id, + commitments.head_data, + relay_parent_number, + )) + } + + /// Check that all the upward messages sent by a candidate pass the acceptance criteria. + pub(crate) fn check_upward_messages( + config: &HostConfiguration, + para: ParaId, + upward_messages: &[UpwardMessage], + ) -> Result<(), UmpAcceptanceCheckErr> { + // Cannot send UMP messages while off-boarding. + if >::is_offboarding(para) { + ensure!(upward_messages.is_empty(), UmpAcceptanceCheckErr::IsOffboarding); + } + + let additional_msgs = upward_messages.len(); + if additional_msgs > config.max_upward_message_num_per_candidate as usize { + return Err(UmpAcceptanceCheckErr::MoreMessagesThanPermitted { + sent: additional_msgs as u32, + permitted: config.max_upward_message_num_per_candidate, + }) + } + + let fp = T::MessageQueue::footprint(AggregateMessageOrigin::Ump(UmpQueueId::Para(para))); + let (para_queue_count, mut para_queue_size) = (fp.count, fp.size); + + if para_queue_count.saturating_add(additional_msgs as u64) > + config.max_upward_queue_count as u64 + { + return Err(UmpAcceptanceCheckErr::CapacityExceeded { + count: para_queue_count.saturating_add(additional_msgs as u64), + limit: config.max_upward_queue_count as u64, + }) + } + + for (idx, msg) in upward_messages.into_iter().enumerate() { + let msg_size = msg.len(); + if msg_size > config.max_upward_message_size as usize { + return Err(UmpAcceptanceCheckErr::MessageSize { + idx: idx as u32, + msg_size: msg_size as u32, + max_size: config.max_upward_message_size, + }) + } + // make sure that the queue is not overfilled. + // we do it here only once since returning false invalidates the whole relay-chain block. + if para_queue_size.saturating_add(msg_size as u64) > config.max_upward_queue_size as u64 + { + return Err(UmpAcceptanceCheckErr::TotalSizeExceeded { + total_size: para_queue_size.saturating_add(msg_size as u64), + limit: config.max_upward_queue_size as u64, + }) + } + para_queue_size.saturating_accrue(msg_size as u64); + } + + Ok(()) + } + + /// Enqueues `upward_messages` from a `para`'s accepted candidate block. + /// + /// This function is infallible since the candidate was already accepted and we therefore need + /// to deal with the messages as given. Messages that are too long will be ignored since such + /// candidates should have already been rejected in [`Self::check_upward_messages`]. + pub(crate) fn receive_upward_messages(para: ParaId, upward_messages: &[Vec]) -> Weight { + let bounded = upward_messages + .iter() + .filter_map(|d| { + BoundedSlice::try_from(&d[..]) + .map_err(|e| { + defensive!("Accepted candidate contains too long msg, len=", d.len()); + e + }) + .ok() + }) + .collect(); + Self::receive_bounded_upward_messages(para, bounded) + } + + /// Enqueues storage-bounded `upward_messages` from a `para`'s accepted candidate block. + pub(crate) fn receive_bounded_upward_messages( + para: ParaId, + messages: Vec>>, + ) -> Weight { + let count = messages.len() as u32; + if count == 0 { + return Weight::zero() + } + + T::MessageQueue::enqueue_messages( + messages.into_iter(), + AggregateMessageOrigin::Ump(UmpQueueId::Para(para)), + ); + let weight = ::WeightInfo::receive_upward_messages(count); + Self::deposit_event(Event::UpwardMessagesReceived { from: para, count }); + weight } /// Cleans up all paras pending availability that the predicate returns true for. @@ -902,17 +1173,6 @@ const fn availability_threshold(n_validators: usize) -> usize { supermajority_threshold(n_validators) } -#[derive(derive_more::From, Debug)] -enum AcceptanceCheckErr { - HeadDataTooLarge, - PrematureCodeUpgrade, - NewCodeTooLarge, - ProcessedDownwardMessages(dmp::ProcessedDownwardMessagesAcceptanceErr), - UpwardMessages(ump::AcceptanceCheckErr), - HrmpWatermark(hrmp::HrmpWatermarkAcceptanceErr), - OutboundHrmp(hrmp::OutboundHrmpAcceptanceErr), -} - impl AcceptanceCheckErr { /// Returns the same error so that it can be threaded through a needle of `DispatchError` and /// ultimately returned from a `Dispatchable`. @@ -930,6 +1190,26 @@ impl AcceptanceCheckErr { } } +impl OnQueueChanged for Pallet { + // Write back the remaining queue capacity into `relay_dispatch_queue_remaining_capacity`. + fn on_queue_changed(origin: AggregateMessageOrigin, count: u64, size: u64) { + let para = match origin { + AggregateMessageOrigin::Ump(UmpQueueId::Para(p)) => p, + }; + // TODO maybe migrate this to u64 + let (count, size) = (count.saturated_into(), size.saturated_into()); + // TODO paritytech/polkadot#6283: Remove all usages of `relay_dispatch_queue_size` + #[allow(deprecated)] + well_known_keys::relay_dispatch_queue_size_typed(para).set((count, size)); + + let config = >::config(); + let remaining_count = config.max_upward_queue_count.saturating_sub(count); + let remaining_size = config.max_upward_queue_size.saturating_sub(size); + well_known_keys::relay_dispatch_queue_remaining_capacity(para) + .set((remaining_count, remaining_size)); + } +} + /// A collection of data required for checking a candidate. pub(crate) struct CandidateCheckContext { config: configuration::HostConfiguration, @@ -965,12 +1245,13 @@ impl CandidateCheckContext { let relay_parent_number = now - One::one(); { - // this should never fail because the para is registered let persisted_validation_data = match crate::util::make_persisted_validation_data::( para_id, relay_parent_number, parent_storage_root, - ) { + ) + .defensive_proof("the para is registered") + { Some(l) => l, None => return Ok(Err(FailedToCreatePVD)), }; @@ -1018,10 +1299,9 @@ impl CandidateCheckContext { ) { log::debug!( target: LOG_TARGET, - "Validation outputs checking during inclusion of a candidate {} for parachain `{}` failed: {:?}", + "Validation outputs checking during inclusion of a candidate {} for parachain `{}` failed", candidate_idx, u32::from(para_id), - err, ); Err(err.strip_into_dispatch_err::())?; }; @@ -1059,10 +1339,18 @@ impl CandidateCheckContext { // check if the candidate passes the messaging acceptance criteria >::check_processed_downward_messages(para_id, processed_downward_messages)?; - >::check_upward_messages(&self.config, para_id, upward_messages)?; + Pallet::::check_upward_messages(&self.config, para_id, upward_messages)?; >::check_hrmp_watermark(para_id, self.relay_parent_number, hrmp_watermark)?; >::check_outbound_hrmp(&self.config, para_id, horizontal_messages)?; Ok(()) } } + +impl QueueFootprinter for Pallet { + type Origin = UmpQueueId; + + fn message_count(origin: Self::Origin) -> u64 { + T::MessageQueue::footprint(AggregateMessageOrigin::Ump(origin)).count + } +} diff --git a/runtime/parachains/src/inclusion/tests.rs b/runtime/parachains/src/inclusion/tests.rs index d962e279df7a..28b7090ce9c9 100644 --- a/runtime/parachains/src/inclusion/tests.rs +++ b/runtime/parachains/src/inclusion/tests.rs @@ -29,6 +29,7 @@ use crate::{ use assert_matches::assert_matches; use frame_support::assert_noop; use keyring::Sr25519Keyring; +use parity_scale_codec::DecodeAll; use primitives::{ BlockNumber, CandidateCommitments, CandidateDescriptor, CollatorId, CompactStatement as Statement, Hash, SignedAvailabilityBitfield, SignedStatement, @@ -159,6 +160,17 @@ pub(crate) fn back_candidate( backed } +pub(crate) fn run_to_block_default_notifications(to: BlockNumber, new_session: Vec) { + run_to_block(to, |b| { + new_session.contains(&b).then_some(SessionChangeNotification { + prev_config: Configuration::config(), + new_config: Configuration::config(), + session_index: ParasShared::session_index() + 1, + ..Default::default() + }) + }); +} + pub(crate) fn run_to_block( to: BlockNumber, new_session: impl Fn(BlockNumber) -> Option>, @@ -177,8 +189,8 @@ pub(crate) fn run_to_block( ¬ification.new_config, notification.validators.clone(), ); - Paras::initializer_on_new_session(¬ification); - ParaInclusion::initializer_on_new_session(¬ification); + let outgoing = Paras::initializer_on_new_session(¬ification); + ParaInclusion::initializer_on_new_session(¬ification, &outgoing); } System::on_finalize(b); @@ -1972,3 +1984,12 @@ fn session_change_wipes() { assert!(>::iter().collect::>().is_empty()); }); } + +/// Assert that the encoding of a known `AggregateMessageOrigin` did not change. +#[test] +fn aggregate_origin_decode_regression_check() { + let ump = AggregateMessageOrigin::Ump(UmpQueueId::Para(u32::MAX.into())); + let raw = (0u8, 0u8, u32::MAX).encode(); + let decoded = AggregateMessageOrigin::decode_all(&mut &raw[..]); + assert_eq!(decoded, Ok(ump), "Migration needed for AggregateMessageOrigin"); +} diff --git a/runtime/parachains/src/initializer.rs b/runtime/parachains/src/initializer.rs index ec50690b3cf7..338392b6e060 100644 --- a/runtime/parachains/src/initializer.rs +++ b/runtime/parachains/src/initializer.rs @@ -22,7 +22,7 @@ use crate::{ configuration::{self, HostConfiguration}, disputes::{self, DisputesHandler as _, SlashingHandler as _}, - dmp, hrmp, inclusion, paras, scheduler, session_info, shared, ump, + dmp, hrmp, inclusion, paras, scheduler, session_info, shared, }; use frame_support::{ traits::{OneSessionHandler, Randomness}, @@ -113,7 +113,6 @@ pub mod pallet { + session_info::Config + disputes::Config + dmp::Config - + ump::Config + hrmp::Config { /// A randomness beacon. @@ -168,7 +167,6 @@ pub mod pallet { T::DisputesHandler::initializer_initialize(now) + T::SlashingHandler::initializer_initialize(now) + dmp::Pallet::::initializer_initialize(now) + - ump::Pallet::::initializer_initialize(now) + hrmp::Pallet::::initializer_initialize(now); HasInitialized::::set(Some(())); @@ -179,7 +177,6 @@ pub mod pallet { fn on_finalize(now: T::BlockNumber) { // reverse initialization order. hrmp::Pallet::::initializer_finalize(); - ump::Pallet::::initializer_finalize(); dmp::Pallet::::initializer_finalize(); T::SlashingHandler::initializer_finalize(); T::DisputesHandler::initializer_finalize(); @@ -263,12 +260,11 @@ impl Pallet { let outgoing_paras = paras::Pallet::::initializer_on_new_session(¬ification); scheduler::Pallet::::initializer_on_new_session(¬ification); - inclusion::Pallet::::initializer_on_new_session(¬ification); + inclusion::Pallet::::initializer_on_new_session(¬ification, &outgoing_paras); session_info::Pallet::::initializer_on_new_session(¬ification); T::DisputesHandler::initializer_on_new_session(¬ification); T::SlashingHandler::initializer_on_new_session(session_index); dmp::Pallet::::initializer_on_new_session(¬ification, &outgoing_paras); - ump::Pallet::::initializer_on_new_session(¬ification, &outgoing_paras); hrmp::Pallet::::initializer_on_new_session(¬ification, &outgoing_paras); } diff --git a/runtime/parachains/src/lib.rs b/runtime/parachains/src/lib.rs index 9b2a49d34986..43c5c6441ad9 100644 --- a/runtime/parachains/src/lib.rs +++ b/runtime/parachains/src/lib.rs @@ -37,7 +37,6 @@ pub mod reward_points; pub mod scheduler; pub mod session_info; pub mod shared; -pub mod ump; pub mod runtime_api_impl; @@ -47,6 +46,8 @@ mod util; mod builder; #[cfg(test)] mod mock; +#[cfg(test)] +mod ump_tests; pub use origin::{ensure_parachain, Origin}; pub use paras::ParaLifecycle; diff --git a/runtime/parachains/src/mock.rs b/runtime/parachains/src/mock.rs index 93be0fd54bce..881fc913c69b 100644 --- a/runtime/parachains/src/mock.rs +++ b/runtime/parachains/src/mock.rs @@ -17,27 +17,31 @@ //! Mocks for all the traits. use crate::{ - configuration, disputes, dmp, hrmp, inclusion, initializer, origin, paras, paras_inherent, - scheduler, session_info, shared, - ump::{self, MessageId, UmpSink}, - ParaId, + configuration, disputes, dmp, hrmp, + inclusion::{self, AggregateMessageOrigin, UmpQueueId}, + initializer, origin, paras, + paras::ParaKind, + paras_inherent, scheduler, session_info, shared, ParaId, }; use frame_support::{ - parameter_types, - traits::{ConstU32, GenesisBuild, ValidatorSet, ValidatorSetWithIdentification}, - weights::Weight, + assert_ok, parameter_types, + traits::{ + Currency, GenesisBuild, ProcessMessage, ProcessMessageError, ValidatorSet, + ValidatorSetWithIdentification, + }, + weights::{Weight, WeightMeter}, }; use frame_support_test::TestRandomness; use parity_scale_codec::Decode; use primitives::{ AuthorityDiscoveryId, Balance, BlockNumber, CandidateHash, Header, Moment, SessionIndex, - UpwardMessage, ValidatorIndex, + UpwardMessage, ValidationCode, ValidatorIndex, }; -use sp_core::H256; +use sp_core::{ConstU32, H256}; use sp_io::TestExternalities; use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup}, + traits::{AccountIdConversion, BlakeTwo256, IdentityLookup}, transaction_validity::TransactionPriority, Permill, }; @@ -54,6 +58,7 @@ frame_support::construct_runtime!( { System: frame_system, Balances: pallet_balances, + MessageQueue: pallet_message_queue, Paras: paras, Configuration: configuration, ParasShared: shared, @@ -62,7 +67,6 @@ frame_support::construct_runtime!( Scheduler: scheduler, Initializer: initializer, Dmp: dmp, - Ump: ump, Hrmp: hrmp, ParachainsOrigin: origin, SessionInfo: session_info, @@ -149,15 +153,10 @@ impl pallet_babe::Config for Test { // session module is the trigger type EpochChangeTrigger = pallet_babe::ExternalTrigger; - type DisabledValidators = (); - type WeightInfo = (); - type MaxAuthorities = MaxAuthorities; - type KeyOwnerProof = sp_core::Void; - type EquivocationReportSystem = (); } @@ -212,6 +211,7 @@ impl crate::paras::Config for Test { type RuntimeEvent = RuntimeEvent; type WeightInfo = crate::paras::TestWeightInfo; type UnsignedPriority = ParasUnsignedPriority; + type QueueFootprinter = ParaInclusion; type NextSessionRotation = TestNextSessionRotation; } @@ -221,14 +221,6 @@ parameter_types! { pub const FirstMessageFactorPercent: u64 = 100; } -impl crate::ump::Config for Test { - type RuntimeEvent = RuntimeEvent; - type UmpSink = TestUmpSink; - type FirstMessageFactorPercent = FirstMessageFactorPercent; - type ExecuteOverweightOrigin = frame_system::EnsureRoot; - type WeightInfo = crate::ump::TestWeightInfo; -} - impl crate::hrmp::Config for Test { type RuntimeOrigin = RuntimeOrigin; type RuntimeEvent = RuntimeEvent; @@ -292,10 +284,62 @@ impl crate::disputes::SlashingHandler for Test { impl crate::scheduler::Config for Test {} +pub struct TestMessageQueueWeight; +impl pallet_message_queue::WeightInfo for TestMessageQueueWeight { + fn ready_ring_knit() -> Weight { + Weight::zero() + } + fn ready_ring_unknit() -> Weight { + Weight::zero() + } + fn service_queue_base() -> Weight { + Weight::zero() + } + fn service_page_base_completion() -> Weight { + Weight::zero() + } + fn service_page_base_no_completion() -> Weight { + Weight::zero() + } + fn service_page_item() -> Weight { + Weight::zero() + } + fn bump_service_head() -> Weight { + Weight::zero() + } + fn reap_page() -> Weight { + Weight::zero() + } + fn execute_overweight_page_removed() -> Weight { + Weight::zero() + } + fn execute_overweight_page_updated() -> Weight { + Weight::zero() + } +} +parameter_types! { + pub const MessageQueueServiceWeight: Weight = Weight::from_all(500); +} + +pub type MessageQueueSize = u32; + +impl pallet_message_queue::Config for Test { + type Size = MessageQueueSize; + type RuntimeEvent = RuntimeEvent; + type WeightInfo = TestMessageQueueWeight; + type MessageProcessor = TestProcessMessage; + type QueueChangeHandler = ParaInclusion; + type HeapSize = ConstU32<65536>; + type MaxStale = ConstU32<8>; + type ServiceWeight = MessageQueueServiceWeight; +} + impl crate::inclusion::Config for Test { + type WeightInfo = (); type RuntimeEvent = RuntimeEvent; type DisputesHandler = Disputes; type RewardValidators = TestRewardValidators; + type MessageQueue = MessageQueue; } impl crate::paras_inherent::Config for Test { @@ -372,39 +416,39 @@ pub fn availability_rewards() -> HashMap { AVAILABILITY_REWARDS.with(|r| r.borrow().clone()) } -std::thread_local! { - static PROCESSED: RefCell> = RefCell::new(vec![]); -} - -/// Return which messages have been processed by `process_upward_message` and clear the buffer. -pub fn take_processed() -> Vec<(ParaId, UpwardMessage)> { - PROCESSED.with(|opt_hook| std::mem::take(&mut *opt_hook.borrow_mut())) +parameter_types! { + pub static Processed: Vec<(ParaId, UpwardMessage)> = vec![]; } /// An implementation of a UMP sink that just records which messages were processed. /// /// A message's weight is defined by the first 4 bytes of its data, which we decode into a /// `u32`. -pub struct TestUmpSink; -impl UmpSink for TestUmpSink { - fn process_upward_message( - actual_origin: ParaId, - actual_msg: &[u8], - max_weight: Weight, - ) -> Result { - let weight = match u32::decode(&mut &actual_msg[..]) { +pub struct TestProcessMessage; +impl ProcessMessage for TestProcessMessage { + type Origin = AggregateMessageOrigin; + + fn process_message( + message: &[u8], + origin: AggregateMessageOrigin, + meter: &mut WeightMeter, + ) -> Result { + let para = match origin { + AggregateMessageOrigin::Ump(UmpQueueId::Para(p)) => p, + }; + + let required = match u32::decode(&mut &message[..]) { Ok(w) => Weight::from_parts(w as u64, w as u64), - Err(_) => return Ok(Weight::zero()), // same as the real `UmpSink` + Err(_) => return Err(ProcessMessageError::Corrupt), // same as the real `ProcessMessage` }; - if weight.any_gt(max_weight) { - let id = sp_io::hashing::blake2_256(actual_msg); - return Err((id, weight)) + if !meter.check_accrue(required) { + return Err(ProcessMessageError::Overweight(required)) } - PROCESSED.with(|opt_hook| { - opt_hook.borrow_mut().push((actual_origin, actual_msg.to_owned())); - }); - Ok(weight) + let mut processed = Processed::get(); + processed.push((para, message.to_vec())); + Processed::set(processed); + Ok(true) } } @@ -463,3 +507,50 @@ pub fn assert_last_event(generic_event: RuntimeEvent) { let frame_system::EventRecord { event, .. } = &events[events.len() - 1]; assert_eq!(event, &system_event); } + +pub fn assert_last_events(generic_events: E) +where + E: DoubleEndedIterator + ExactSizeIterator, +{ + for (i, (got, want)) in frame_system::Pallet::::events() + .into_iter() + .rev() + .map(|e| e.event) + .zip(generic_events.rev().map(::RuntimeEvent::from)) + .rev() + .enumerate() + { + assert_eq!((i, got), (i, want)); + } +} + +pub(crate) fn register_parachain_with_balance(id: ParaId, balance: Balance) { + let validation_code: ValidationCode = vec![1].into(); + assert_ok!(Paras::schedule_para_initialize( + id, + crate::paras::ParaGenesisArgs { + para_kind: ParaKind::Parachain, + genesis_head: vec![1].into(), + validation_code: validation_code.clone(), + }, + )); + + assert_ok!(Paras::add_trusted_validation_code(RuntimeOrigin::root(), validation_code)); + ::Currency::make_free_balance_be( + &id.into_account_truncating(), + balance, + ); +} + +pub(crate) fn register_parachain(id: ParaId) { + register_parachain_with_balance(id, 1000); +} + +pub(crate) fn deregister_parachain(id: ParaId) { + assert_ok!(Paras::schedule_para_cleanup(id)); +} + +/// Calls `schedule_para_cleanup` in a new storage transactions, since it assumes rollback on error. +pub(crate) fn try_deregister_parachain(id: ParaId) -> crate::DispatchResult { + frame_support::storage::transactional::with_storage_layer(|| Paras::schedule_para_cleanup(id)) +} diff --git a/runtime/parachains/src/paras/mod.rs b/runtime/parachains/src/paras/mod.rs index 77cfbae5c8b9..bf2a3790385b 100644 --- a/runtime/parachains/src/paras/mod.rs +++ b/runtime/parachains/src/paras/mod.rs @@ -107,7 +107,12 @@ //! ``` //! -use crate::{configuration, initializer::SessionChangeNotification, shared}; +use crate::{ + configuration, + inclusion::{QueueFootprinter, UmpQueueId}, + initializer::SessionChangeNotification, + shared, +}; use bitvec::{order::Lsb0 as BitOrderLsb0, vec::BitVec}; use frame_support::{pallet_prelude::*, traits::EstimateNextSessionRotation}; use frame_system::pallet_prelude::*; @@ -551,6 +556,12 @@ pub mod pallet { type NextSessionRotation: EstimateNextSessionRotation; + /// Retrieve how many UMP messages are enqueued for this para-chain. + /// + /// This is used to judge whether or not a para-chain can offboard. Per default this should + /// be set to the `ParaInclusion` pallet. + type QueueFootprinter: QueueFootprinter; + /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; } @@ -1653,12 +1664,13 @@ impl Pallet { /// /// - para is not a stable parachain or parathread (i.e. [`ParaLifecycle::is_stable`] is `false`) /// - para has a pending upgrade. + /// - para has unprocessed messages in its UMP queue. /// /// No-op if para is not registered at all. pub(crate) fn schedule_para_cleanup(id: ParaId) -> DispatchResult { // Disallow offboarding in case there is a PVF pre-checking in progress. // - // This is not a fundamential limitation but rather simplification: it allows us to get + // This is not a fundamental limitation but rather simplification: it allows us to get // away without introducing additional logic for pruning and, more importantly, enacting // ongoing PVF pre-checking votes. It also removes some nasty edge cases. // @@ -1683,7 +1695,7 @@ impl Pallet { Some(ParaLifecycle::Parachain) => { ParaLifecycles::::insert(&id, ParaLifecycle::OffboardingParachain); }, - _ => return Err(Error::::CannotOffboard)?, + _ => return Err(Error::::CannotOffboard.into()), } let scheduled_session = Self::scheduled_session(); @@ -1693,6 +1705,10 @@ impl Pallet { } }); + if ::QueueFootprinter::message_count(UmpQueueId::Para(id)) != 0 { + return Err(Error::::CannotOffboard.into()) + } + Ok(()) } @@ -1986,6 +2002,13 @@ impl Pallet { } } + /// Returns whether the given ID refers to a para that is offboarding. + /// + /// An invalid or non-offboarding para ID will return `false`. + pub fn is_offboarding(id: ParaId) -> bool { + ParaLifecycles::::get(&id).map_or(false, |state| state.is_offboarding()) + } + /// Whether a para ID corresponds to any live parachain. /// /// Includes parachains which will downgrade to a parathread in the future. diff --git a/runtime/parachains/src/paras_inherent/mod.rs b/runtime/parachains/src/paras_inherent/mod.rs index 550e1fdec38c..e71b5e9cb8a6 100644 --- a/runtime/parachains/src/paras_inherent/mod.rs +++ b/runtime/parachains/src/paras_inherent/mod.rs @@ -29,7 +29,7 @@ use crate::{ initializer, metrics::METRICS, scheduler::{self, CoreAssignment, FreedReason}, - shared, ump, ParaId, + shared, ParaId, }; use bitvec::prelude::BitVec; use frame_support::{ @@ -531,10 +531,6 @@ impl Pallet { // Note which of the scheduled cores were actually occupied by a backed candidate. >::occupied(&occupied); - // Give some time slice to dispatch pending upward messages. - // this is max config.ump_service_total_weight - let _ump_weight = >::process_pending_upward_messages(); - METRICS.on_after_filter(total_consumed_weight.ref_time()); Ok(Some(total_consumed_weight).into()) diff --git a/runtime/parachains/src/runtime_api_impl/v4.rs b/runtime/parachains/src/runtime_api_impl/v4.rs index c9cc637852e6..20f62f520a19 100644 --- a/runtime/parachains/src/runtime_api_impl/v4.rs +++ b/runtime/parachains/src/runtime_api_impl/v4.rs @@ -331,14 +331,18 @@ where >::read_events_no_consensus() .into_iter() .filter_map(|record| extract_event(record.event)) - .map(|event| match event { - RawEvent::::CandidateBacked(c, h, core, group) => - CandidateEvent::CandidateBacked(c, h, core, group), - RawEvent::::CandidateIncluded(c, h, core, group) => - CandidateEvent::CandidateIncluded(c, h, core, group), - RawEvent::::CandidateTimedOut(c, h, core) => - CandidateEvent::CandidateTimedOut(c, h, core), - RawEvent::::__Ignore(_, _) => unreachable!("__Ignore cannot be used"), + .filter_map(|event| { + Some(match event { + RawEvent::::CandidateBacked(c, h, core, group) => + CandidateEvent::CandidateBacked(c, h, core, group), + RawEvent::::CandidateIncluded(c, h, core, group) => + CandidateEvent::CandidateIncluded(c, h, core, group), + RawEvent::::CandidateTimedOut(c, h, core) => + CandidateEvent::CandidateTimedOut(c, h, core), + // Not needed for candidate events. + RawEvent::::UpwardMessagesReceived { .. } => return None, + RawEvent::::__Ignore(_, _) => unreachable!("__Ignore cannot be used"), + }) }) .collect() } diff --git a/runtime/parachains/src/ump.rs b/runtime/parachains/src/ump.rs deleted file mode 100644 index eeaf1fca176d..000000000000 --- a/runtime/parachains/src/ump.rs +++ /dev/null @@ -1,765 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -use crate::{ - configuration::{self, HostConfiguration}, - initializer, -}; -use frame_support::{pallet_prelude::*, traits::EnsureOrigin}; -use frame_system::pallet_prelude::*; -use polkadot_parachain::primitives::UpwardMessages; -use primitives::{Id as ParaId, UpwardMessage}; -use sp_std::{collections::btree_map::BTreeMap, fmt, marker::PhantomData, mem, prelude::*}; -use xcm::latest::Outcome; - -pub use pallet::*; - -/// Maximum value that `config.max_upward_message_size` can be set to -/// -/// This is used for benchmarking sanely bounding relevant storage items. It is expected from the `configurations` -/// pallet to check these values before setting. -pub const MAX_UPWARD_MESSAGE_SIZE_BOUND: u32 = 50 * 1024; -/// Maximum amount of overweight messages that can exist in the queue at any given time. -pub const MAX_OVERWEIGHT_MESSAGES: u32 = 1000; - -#[cfg(feature = "runtime-benchmarks")] -mod benchmarking; -pub mod migration; - -#[cfg(test)] -pub(crate) mod tests; - -/// All upward messages coming from parachains will be funneled into an implementation of this trait. -/// -/// The message is opaque from the perspective of UMP. The message size can range from 0 to -/// `config.max_upward_message_size`. -/// -/// It's up to the implementation of this trait to decide what to do with a message as long as it -/// returns the amount of weight consumed in the process of handling. Ignoring a message is a valid -/// strategy. -/// -/// There are no guarantees on how much time it takes for the message sent by a candidate to end up -/// in the sink after the candidate was enacted. That typically depends on the UMP traffic, the sizes -/// of upward messages and the configuration of UMP. -/// -/// It is possible that by the time the message is sank the origin parachain was offboarded. It is -/// up to the implementer to check that if it cares. -pub trait UmpSink { - /// Process an incoming upward message and return the amount of weight it consumed, or `None` if - /// it did not begin processing a message since it would otherwise exceed `max_weight`. - /// - /// See the trait docs for more details. - fn process_upward_message( - origin: ParaId, - msg: &[u8], - max_weight: Weight, - ) -> Result; -} - -/// An implementation of a sink that just swallows the message without consuming any weight. Returns -/// `Some(0)` indicating that no messages existed for it to process. -impl UmpSink for () { - fn process_upward_message( - _: ParaId, - _: &[u8], - _: Weight, - ) -> Result { - Ok(Weight::zero()) - } -} - -/// Simple type used to identify messages for the purpose of reporting events. Secure if and only -/// if the message content is unique. -pub type MessageId = [u8; 32]; - -/// Index used to identify overweight messages. -pub type OverweightIndex = u64; - -/// A specific implementation of a `UmpSink` where messages are in the XCM format -/// and will be forwarded to the XCM Executor. -pub struct XcmSink(PhantomData<(XcmExecutor, Config)>); - -/// Returns a [`MessageId`] for the given upward message payload. -fn upward_message_id(data: &[u8]) -> MessageId { - sp_io::hashing::blake2_256(data) -} - -impl, C: Config> UmpSink - for XcmSink -{ - fn process_upward_message( - origin: ParaId, - mut data: &[u8], - max_weight: Weight, - ) -> Result { - use parity_scale_codec::DecodeLimit; - use xcm::{ - latest::{Error as XcmError, Junction, Xcm}, - VersionedXcm, - }; - - let id = upward_message_id(data); - let maybe_msg_and_weight = VersionedXcm::::decode_all_with_depth_limit( - xcm::MAX_XCM_DECODE_DEPTH, - &mut data, - ) - .map(|xcm| { - ( - Xcm::::try_from(xcm), - // NOTE: We are overestimating slightly here. - // The benchmark is timing this whole function with different message sizes and a NOOP extrinsic to - // measure the size-dependent weight. But as we use the weight funtion **in** the benchmarked funtion we - // are taking call and control-flow overhead into account twice. - ::WeightInfo::process_upward_message(data.len() as u32), - ) - }); - match maybe_msg_and_weight { - Err(_) => { - Pallet::::deposit_event(Event::InvalidFormat(id)); - Ok(Weight::zero()) - }, - Ok((Err(()), weight_used)) => { - Pallet::::deposit_event(Event::UnsupportedVersion(id)); - Ok(weight_used) - }, - Ok((Ok(xcm_message), weight_used)) => { - let xcm_junction = Junction::Parachain(origin.into()); - let outcome = XcmExecutor::execute_xcm(xcm_junction, xcm_message, id, max_weight); - match outcome { - Outcome::Error(XcmError::WeightLimitReached(required)) => Err((id, required)), - outcome => { - let outcome_weight = outcome.weight_used(); - Pallet::::deposit_event(Event::ExecutedUpward(id, outcome)); - Ok(weight_used.saturating_add(outcome_weight)) - }, - } - }, - } - } -} - -/// An error returned by [`check_upward_messages`] that indicates a violation of one of acceptance -/// criteria rules. -pub enum AcceptanceCheckErr { - MoreMessagesThanPermitted { sent: u32, permitted: u32 }, - MessageSize { idx: u32, msg_size: u32, max_size: u32 }, - CapacityExceeded { count: u32, limit: u32 }, - TotalSizeExceeded { total_size: u32, limit: u32 }, -} - -impl fmt::Debug for AcceptanceCheckErr { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - match *self { - AcceptanceCheckErr::MoreMessagesThanPermitted { sent, permitted } => write!( - fmt, - "more upward messages than permitted by config ({} > {})", - sent, permitted, - ), - AcceptanceCheckErr::MessageSize { idx, msg_size, max_size } => write!( - fmt, - "upward message idx {} larger than permitted by config ({} > {})", - idx, msg_size, max_size, - ), - AcceptanceCheckErr::CapacityExceeded { count, limit } => write!( - fmt, - "the ump queue would have more items than permitted by config ({} > {})", - count, limit, - ), - AcceptanceCheckErr::TotalSizeExceeded { total_size, limit } => write!( - fmt, - "the ump queue would have grown past the max size permitted by config ({} > {})", - total_size, limit, - ), - } - } -} - -/// Weight information of this pallet. -pub trait WeightInfo { - fn service_overweight() -> Weight; - fn process_upward_message(s: u32) -> Weight; - fn clean_ump_after_outgoing() -> Weight; -} - -/// fallback implementation -pub struct TestWeightInfo; -impl WeightInfo for TestWeightInfo { - fn service_overweight() -> Weight { - Weight::MAX - } - - fn process_upward_message(_msg_size: u32) -> Weight { - Weight::MAX - } - - fn clean_ump_after_outgoing() -> Weight { - Weight::MAX - } -} - -#[frame_support::pallet] -pub mod pallet { - use super::*; - - const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); - - #[pallet::pallet] - #[pallet::without_storage_info] - #[pallet::storage_version(STORAGE_VERSION)] - pub struct Pallet(_); - - #[pallet::config] - pub trait Config: frame_system::Config + configuration::Config { - /// The aggregate event. - type RuntimeEvent: From + IsType<::RuntimeEvent>; - - /// A place where all received upward messages are funneled. - type UmpSink: UmpSink; - - /// The factor by which the weight limit it multiplied for the first UMP message to execute with. - /// - /// An amount less than 100 keeps more available weight in the queue for messages after the first, and potentially - /// stalls the queue in doing so. More than 100 will provide additional weight for the first message only. - /// - /// Generally you'll want this to be a bit more - 150 or 200 would be good values. - type FirstMessageFactorPercent: Get; - - /// Origin which is allowed to execute overweight messages. - type ExecuteOverweightOrigin: EnsureOrigin; - - /// Weight information for extrinsics in this pallet. - type WeightInfo: WeightInfo; - } - - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event { - /// Upward message is invalid XCM. - /// \[ id \] - InvalidFormat(MessageId), - /// Upward message is unsupported version of XCM. - /// \[ id \] - UnsupportedVersion(MessageId), - /// Upward message executed with the given outcome. - /// \[ id, outcome \] - ExecutedUpward(MessageId, Outcome), - /// The weight limit for handling upward messages was reached. - /// \[ id, remaining, required \] - WeightExhausted(MessageId, Weight, Weight), - /// Some upward messages have been received and will be processed. - /// \[ para, count, size \] - UpwardMessagesReceived(ParaId, u32, u32), - /// The weight budget was exceeded for an individual upward message. - /// - /// This message can be later dispatched manually using `service_overweight` dispatchable - /// using the assigned `overweight_index`. - /// - /// \[ para, id, overweight_index, required \] - OverweightEnqueued(ParaId, MessageId, OverweightIndex, Weight), - /// Upward message from the overweight queue was executed with the given actual weight - /// used. - /// - /// \[ overweight_index, used \] - OverweightServiced(OverweightIndex, Weight), - } - - #[pallet::error] - pub enum Error { - /// The message index given is unknown. - UnknownMessageIndex, - /// The amount of weight given is possibly not enough for executing the message. - WeightOverLimit, - } - - /// The messages waiting to be handled by the relay-chain originating from a certain parachain. - /// - /// Note that some upward messages might have been already processed by the inclusion logic. E.g. - /// channel management messages. - /// - /// The messages are processed in FIFO order. - #[pallet::storage] - pub type RelayDispatchQueues = - StorageMap<_, Twox64Concat, ParaId, Vec, ValueQuery>; - - /// Size of the dispatch queues. Caches sizes of the queues in `RelayDispatchQueue`. - /// - /// First item in the tuple is the count of messages and second - /// is the total length (in bytes) of the message payloads. - /// - /// Note that this is an auxiliary mapping: it's possible to tell the byte size and the number of - /// messages only looking at `RelayDispatchQueues`. This mapping is separate to avoid the cost of - /// loading the whole message queue if only the total size and count are required. - /// - /// Invariant: - /// - The set of keys should exactly match the set of keys of `RelayDispatchQueues`. - // 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 RelayDispatchQueueSize = - StorageMap<_, Twox64Concat, ParaId, (u32, u32), ValueQuery>; - - /// The ordered list of `ParaId`s that have a `RelayDispatchQueue` entry. - /// - /// Invariant: - /// - The set of items from this vector should be exactly the set of the keys in - /// `RelayDispatchQueues` and `RelayDispatchQueueSize`. - #[pallet::storage] - pub type NeedsDispatch = StorageValue<_, Vec, ValueQuery>; - - /// This is the para that gets will get dispatched first during the next upward dispatchable queue - /// execution round. - /// - /// Invariant: - /// - If `Some(para)`, then `para` must be present in `NeedsDispatch`. - #[pallet::storage] - pub type NextDispatchRoundStartWith = StorageValue<_, ParaId>; - - /// The messages that exceeded max individual message weight budget. - /// - /// These messages stay there until manually dispatched. - #[pallet::storage] - pub type Overweight = - CountedStorageMap<_, Twox64Concat, OverweightIndex, (ParaId, Vec), OptionQuery>; - - /// The number of overweight messages ever recorded in `Overweight` (and thus the lowest free - /// index). - #[pallet::storage] - pub type OverweightCount = StorageValue<_, OverweightIndex, ValueQuery>; - - #[pallet::call] - impl Pallet { - /// Service a single overweight upward message. - /// - /// - `origin`: Must pass `ExecuteOverweightOrigin`. - /// - `index`: The index of the overweight message to service. - /// - `weight_limit`: The amount of weight that message execution may take. - /// - /// Errors: - /// - `UnknownMessageIndex`: Message of `index` is unknown. - /// - `WeightOverLimit`: Message execution may use greater than `weight_limit`. - /// - /// Events: - /// - `OverweightServiced`: On success. - #[pallet::call_index(0)] - #[pallet::weight(weight_limit.saturating_add(::WeightInfo::service_overweight()))] - pub fn service_overweight( - origin: OriginFor, - index: OverweightIndex, - weight_limit: Weight, - ) -> DispatchResultWithPostInfo { - T::ExecuteOverweightOrigin::ensure_origin(origin)?; - - let (sender, data) = - Overweight::::get(index).ok_or(Error::::UnknownMessageIndex)?; - let used = T::UmpSink::process_upward_message(sender, &data[..], weight_limit) - .map_err(|_| Error::::WeightOverLimit)?; - Overweight::::remove(index); - Self::deposit_event(Event::OverweightServiced(index, used)); - Ok(Some(used.saturating_add(::WeightInfo::service_overweight())).into()) - } - } -} - -/// Routines related to the upward message passing. -impl Pallet { - /// Block initialization logic, called by initializer. - pub(crate) fn initializer_initialize(_now: T::BlockNumber) -> Weight { - Weight::zero() - } - - /// Block finalization logic, called by initializer. - pub(crate) fn initializer_finalize() {} - - /// Called by the initializer to note that a new session has started. - pub(crate) fn initializer_on_new_session( - _notification: &initializer::SessionChangeNotification, - outgoing_paras: &[ParaId], - ) -> Weight { - Self::perform_outgoing_para_cleanup(outgoing_paras) - } - - /// Iterate over all paras that were noted for offboarding and remove all the data - /// associated with them. - fn perform_outgoing_para_cleanup(outgoing: &[ParaId]) -> Weight { - let mut weight: Weight = Weight::zero(); - for outgoing_para in outgoing { - weight = weight.saturating_add(Self::clean_ump_after_outgoing(outgoing_para)); - } - weight - } - - /// Remove all relevant storage items for an outgoing parachain. - pub(crate) fn clean_ump_after_outgoing(outgoing_para: &ParaId) -> Weight { - RelayDispatchQueueSize::::remove(outgoing_para); - RelayDispatchQueues::::remove(outgoing_para); - - // Remove the outgoing para from the `NeedsDispatch` list and from - // `NextDispatchRoundStartWith`. - // - // That's needed for maintaining invariant that `NextDispatchRoundStartWith` points to an - // existing item in `NeedsDispatch`. - NeedsDispatch::::mutate(|v| { - if let Ok(i) = v.binary_search(outgoing_para) { - v.remove(i); - } - }); - NextDispatchRoundStartWith::::mutate(|v| *v = v.filter(|p| p == outgoing_para)); - - ::WeightInfo::clean_ump_after_outgoing() - } - - /// Check that all the upward messages sent by a candidate pass the acceptance criteria. Returns - /// false, if any of the messages doesn't pass. - pub(crate) fn check_upward_messages( - config: &HostConfiguration, - para: ParaId, - upward_messages: &[UpwardMessage], - ) -> Result<(), AcceptanceCheckErr> { - if upward_messages.len() as u32 > config.max_upward_message_num_per_candidate { - return Err(AcceptanceCheckErr::MoreMessagesThanPermitted { - sent: upward_messages.len() as u32, - permitted: config.max_upward_message_num_per_candidate, - }) - } - - let (mut para_queue_count, mut para_queue_size) = RelayDispatchQueueSize::::get(¶); - - for (idx, msg) in upward_messages.into_iter().enumerate() { - let msg_size = msg.len() as u32; - if msg_size > config.max_upward_message_size { - return Err(AcceptanceCheckErr::MessageSize { - idx: idx as u32, - msg_size, - max_size: config.max_upward_message_size, - }) - } - para_queue_count += 1; - para_queue_size += msg_size; - } - - // make sure that the queue is not overfilled. - // we do it here only once since returning false invalidates the whole relay-chain block. - if para_queue_count > config.max_upward_queue_count { - return Err(AcceptanceCheckErr::CapacityExceeded { - count: para_queue_count, - limit: config.max_upward_queue_count, - }) - } - if para_queue_size > config.max_upward_queue_size { - return Err(AcceptanceCheckErr::TotalSizeExceeded { - total_size: para_queue_size, - limit: config.max_upward_queue_size, - }) - } - - Ok(()) - } - - /// Enqueues `upward_messages` from a `para`'s accepted candidate block. - pub(crate) fn receive_upward_messages(para: ParaId, upward_messages: UpwardMessages) -> Weight { - let mut weight = Weight::zero(); - - if !upward_messages.is_empty() { - let (extra_count, extra_size) = upward_messages - .iter() - .fold((0, 0), |(cnt, size), d| (cnt + 1, size + d.len() as u32)); - - RelayDispatchQueues::::mutate(¶, |v| v.extend(upward_messages.into_iter())); - - RelayDispatchQueueSize::::mutate(¶, |(ref mut cnt, ref mut size)| { - *cnt += extra_count; - *size += extra_size; - }); - - NeedsDispatch::::mutate(|v| { - if let Err(i) = v.binary_search(¶) { - v.insert(i, para); - } - }); - - // NOTE: The actual computation is not accounted for. It should be benchmarked. - weight += T::DbWeight::get().reads_writes(3, 3); - - Self::deposit_event(Event::UpwardMessagesReceived(para, extra_count, extra_size)); - } - - weight - } - - /// Devote some time into dispatching pending upward messages. - pub(crate) fn process_pending_upward_messages() -> Weight { - const MAX_MESSAGES_PER_BLOCK: u8 = 10; - let mut messages_processed = 0; - let mut weight_used = Weight::zero(); - - let config = >::config(); - let mut cursor = NeedsDispatchCursor::new::(); - let mut queue_cache = QueueCache::new(); - - while let Some(dispatchee) = cursor.peek() { - if weight_used.any_gte(config.ump_service_total_weight) || - messages_processed >= MAX_MESSAGES_PER_BLOCK - { - // Temporarily allow for processing of a max of 10 messages per block, until we - // properly account for proof size weights. - // - // Then check whether we've reached or overshoot the - // preferred weight for the dispatching stage. - // - // if so - bail. - break - } - let max_weight = if weight_used == Weight::zero() { - // we increase the amount of weight that we're allowed to use on the first message to try to prevent - // the possibility of blockage of the queue. - config - .ump_service_total_weight - .saturating_mul(T::FirstMessageFactorPercent::get()) / - 100 - } else { - config.ump_service_total_weight - weight_used - }; - - // attempt to process the next message from the queue of the dispatchee; if not beyond - // our remaining weight limit, then consume it. - let maybe_next = queue_cache.peek_front::(dispatchee); - if let Some(upward_message) = maybe_next { - messages_processed += 1; - match T::UmpSink::process_upward_message(dispatchee, upward_message, max_weight) { - Ok(used) => { - weight_used += used; - let _ = queue_cache.consume_front::(dispatchee); - }, - Err((id, required)) => { - let is_under_limit = Overweight::::count() < MAX_OVERWEIGHT_MESSAGES; - weight_used.saturating_accrue(T::DbWeight::get().reads(1)); - if required.any_gt(config.ump_max_individual_weight) && is_under_limit { - // overweight - add to overweight queue and continue with message - // execution consuming the message. - let upward_message = queue_cache.consume_front::(dispatchee).expect( - "`consume_front` should return the same msg as `peek_front`;\ - if we get into this branch then `peek_front` returned `Some`;\ - thus `upward_message` cannot be `None`; qed", - ); - let index = Self::stash_overweight(dispatchee, upward_message); - Self::deposit_event(Event::OverweightEnqueued( - dispatchee, id, index, required, - )); - } else { - // we process messages in order and don't drop them if we run out of weight, - // so need to break here without calling `consume_front`. - Self::deposit_event(Event::WeightExhausted(id, max_weight, required)); - break - } - }, - } - } - - if queue_cache.is_empty::(dispatchee) { - // the queue is empty now - this para doesn't need attention anymore. - cursor.remove(); - } else { - cursor.advance(); - } - } - - cursor.flush::(); - queue_cache.flush::(); - - weight_used - } - - /// Puts a given upward message into the list of overweight messages allowing it to be executed - /// later. - fn stash_overweight(sender: ParaId, upward_message: Vec) -> OverweightIndex { - let index = OverweightCount::::mutate(|count| { - let index = *count; - *count += 1; - index - }); - - Overweight::::insert(index, (sender, upward_message)); - index - } -} - -/// To avoid constant fetching, deserializing and serialization the queues are cached. -/// -/// After an item dequeued from a queue for the first time, the queue is stored in this struct -/// rather than being serialized and persisted. -/// -/// This implementation works best when: -/// -/// 1. when the queues are shallow -/// 2. the dispatcher makes more than one cycle -/// -/// if the queues are deep and there are many we would load and keep the queues for a long time, -/// thus increasing the peak memory consumption of the wasm runtime. Under such conditions persisting -/// queues might play better since it's unlikely that they are going to be requested once more. -/// -/// On the other hand, the situation when deep queues exist and it takes more than one dispatcher -/// cycle to traverse the queues is already sub-optimal and better be avoided. -/// -/// This struct is not supposed to be dropped but rather to be consumed by [`flush`]. -struct QueueCache(BTreeMap); - -struct QueueCacheEntry { - queue: Vec, - total_size: u32, - consumed_count: usize, - consumed_size: usize, -} - -impl QueueCache { - fn new() -> Self { - Self(BTreeMap::new()) - } - - fn ensure_cached(&mut self, para: ParaId) -> &mut QueueCacheEntry { - self.0.entry(para).or_insert_with(|| { - let queue = RelayDispatchQueues::::get(¶); - let (_, total_size) = RelayDispatchQueueSize::::get(¶); - QueueCacheEntry { queue, total_size, consumed_count: 0, consumed_size: 0 } - }) - } - - /// Returns the message at the front of `para`'s queue, or `None` if the queue is empty. - /// - /// Does not mutate the queue. - fn peek_front(&mut self, para: ParaId) -> Option<&UpwardMessage> { - let entry = self.ensure_cached::(para); - entry.queue.get(entry.consumed_count) - } - - /// Attempts to remove one message from the front of `para`'s queue. If the queue is empty, then - /// does nothing. - fn consume_front(&mut self, para: ParaId) -> Option { - let cache_entry = self.ensure_cached::(para); - - match cache_entry.queue.get_mut(cache_entry.consumed_count) { - Some(msg) => { - cache_entry.consumed_count += 1; - cache_entry.consumed_size += msg.len(); - - Some(mem::take(msg)) - }, - None => None, - } - } - - /// Returns if the queue for the given para is empty. - /// - /// That is, if this returns `true` then the next call to [`peek_front`] will return `None`. - /// - /// Does not mutate the queue. - fn is_empty(&mut self, para: ParaId) -> bool { - let cache_entry = self.ensure_cached::(para); - cache_entry.consumed_count >= cache_entry.queue.len() - } - - /// Flushes the updated queues into the storage. - fn flush(self) { - // NOTE we use an explicit method here instead of Drop impl because it has unwanted semantics - // within runtime. It is dangerous to use because of double-panics and flushing on a panic - // is not necessary as well. - for (para, entry) in self.0 { - if entry.consumed_count >= entry.queue.len() { - // remove the entries altogether. - RelayDispatchQueues::::remove(¶); - RelayDispatchQueueSize::::remove(¶); - } else if entry.consumed_count > 0 { - RelayDispatchQueues::::insert(¶, &entry.queue[entry.consumed_count..]); - let count = (entry.queue.len() - entry.consumed_count) as u32; - let size = entry.total_size.saturating_sub(entry.consumed_size as u32); - RelayDispatchQueueSize::::insert(¶, (count, size)); - } - } - } -} - -/// A cursor that iterates over all entries in `NeedsDispatch`. -/// -/// This cursor will start with the para indicated by `NextDispatchRoundStartWith` storage entry. -/// This cursor is cyclic meaning that after reaching the end it will jump to the beginning. Unlike -/// an iterator, this cursor allows removing items during the iteration. -/// -/// Each iteration cycle *must be* concluded with a call to either `advance` or `remove`. -/// -/// This struct is not supposed to be dropped but rather to be consumed by [`flush`]. -#[derive(Debug)] -struct NeedsDispatchCursor { - needs_dispatch: Vec, - index: usize, -} - -impl NeedsDispatchCursor { - fn new() -> Self { - let needs_dispatch: Vec = NeedsDispatch::::get(); - let start_with = NextDispatchRoundStartWith::::get(); - - let initial_index = match start_with { - Some(para) => match needs_dispatch.binary_search(¶) { - Ok(found_index) => found_index, - Err(_supposed_index) => { - // well that's weird because we maintain an invariant that - // `NextDispatchRoundStartWith` must point into one of the items in - // `NeedsDispatch`. - // - // let's select 0 as the starting index as a safe bet. - debug_assert!(false); - 0 - }, - }, - None => 0, - }; - - Self { needs_dispatch, index: initial_index } - } - - /// Returns the item the cursor points to. - fn peek(&self) -> Option { - self.needs_dispatch.get(self.index).cloned() - } - - /// Moves the cursor to the next item. - fn advance(&mut self) { - if self.needs_dispatch.is_empty() { - return - } - self.index = (self.index + 1) % self.needs_dispatch.len(); - } - - /// Removes the item under the cursor. - fn remove(&mut self) { - if self.needs_dispatch.is_empty() { - return - } - let _ = self.needs_dispatch.remove(self.index); - - // we might've removed the last element and that doesn't necessarily mean that `needs_dispatch` - // became empty. Reposition the cursor in this case to the beginning. - if self.needs_dispatch.get(self.index).is_none() { - self.index = 0; - } - } - - /// Flushes the dispatcher state into the persistent storage. - fn flush(self) { - let next_one = self.peek(); - NextDispatchRoundStartWith::::set(next_one); - NeedsDispatch::::put(self.needs_dispatch); - } -} diff --git a/runtime/parachains/src/ump/benchmarking.rs b/runtime/parachains/src/ump/benchmarking.rs deleted file mode 100644 index 561104980960..000000000000 --- a/runtime/parachains/src/ump/benchmarking.rs +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -use super::{Pallet as Ump, *}; -use frame_system::RawOrigin; -use xcm::prelude::*; - -fn assert_last_event_type(generic_event: ::RuntimeEvent) { - let events = frame_system::Pallet::::events(); - let system_event: ::RuntimeEvent = generic_event.into(); - // compare to the last event record - let frame_system::EventRecord { event, .. } = &events[events.len() - 1]; - assert_eq!(sp_std::mem::discriminant(event), sp_std::mem::discriminant(&system_event)); -} - -fn queue_upward_msg( - host_conf: &HostConfiguration, - para: ParaId, - msg: UpwardMessage, -) { - let len = msg.len() as u32; - let msgs: UpwardMessages = vec![msg].try_into().unwrap(); - Ump::::check_upward_messages(host_conf, para, &msgs).unwrap(); - let _ = Ump::::receive_upward_messages(para, msgs); - assert_last_event_type::(Event::UpwardMessagesReceived(para, 1, len).into()); -} - -// Create a message with at least `size` bytes encoded length -fn create_message_min_size(size: u32) -> Vec { - // Create a message with an empty remark call to determine the encoding overhead - let msg_size_empty_transact = VersionedXcm::::from(Xcm::(vec![Transact { - origin_kind: OriginKind::SovereignAccount, - require_weight_at_most: Weight::MAX, - call: frame_system::Call::::remark_with_event { remark: vec![] }.encode().into(), - }])) - .encode() - .len(); - - // Create a message with a remark call of just the size required to make the whole encoded message the requested size - let size = size.saturating_sub(msg_size_empty_transact as u32) as usize; - let mut remark = Vec::new(); - remark.resize(size, 0u8); - let msg = VersionedXcm::::from(Xcm::(vec![Transact { - origin_kind: OriginKind::SovereignAccount, - require_weight_at_most: Weight::MAX, - call: frame_system::Call::::remark_with_event { remark }.encode().into(), - }])) - .encode(); - - assert!(msg.len() >= size); - msg -} - -fn create_message_overweight() -> Vec { - // We use a `set_code` Call because it - let call = frame_system::Call::::set_code { code: vec![] }; - VersionedXcm::::from(Xcm::(vec![Transact { - origin_kind: OriginKind::Superuser, - require_weight_at_most: Weight::MAX / 2, - call: call.encode().into(), - }])) - .encode() -} - -frame_benchmarking::benchmarks! { - // NOTE: We are overestimating slightly here. - // The benchmark is timing this whole function with different message sizes and a NOOP extrinsic to - // measure the size-dependent weight. But as we use the weight function **in** the benchmarked function we - // are taking call and control-flow overhead into account twice. - process_upward_message { - let s in 0..MAX_UPWARD_MESSAGE_SIZE_BOUND; - let para = ParaId::from(1978); - let data = create_message_min_size::(s); - }: { - assert!(T::UmpSink::process_upward_message(para, &data[..], Weight::MAX).is_ok()); - } - - clean_ump_after_outgoing { - // max number of queued messages. - let count = configuration::ActiveConfig::::get().max_upward_queue_count; - let host_conf = configuration::ActiveConfig::::get(); - let msg = create_message_min_size::(0); - // Start with the block number 1. This is needed because should an event be - // emitted during the genesis block they will be implicitly wiped. - frame_system::Pallet::::set_block_number(1u32.into()); - // fill the queue, each message has it's own para-id. - for id in 0..count { - queue_upward_msg::(&host_conf, ParaId::from(id), msg.clone()); - } - }: { - Ump::::clean_ump_after_outgoing(&ParaId::from(0)); - } - - service_overweight { - let host_conf = configuration::ActiveConfig::::get(); - let para = ParaId::from(1978); - // The message's weight does not really matter here, as we add service_overweight's - // max_weight parameter to the extrinsic's weight in the weight calculation. - // The size of the message influences decoding time, so we create a min-sized message here - // and take the decoding weight into account by adding it to the extrinsic execution weight - // in the process_upward_message function. - let msg = create_message_overweight::(); - - // This just makes sure that 0 is not a valid index and we can use it later on. - let _ = Ump::::service_overweight(RawOrigin::Root.into(), 0, Weight::from_parts(1000, 1000)); - // Start with the block number 1. This is needed because should an event be - // emitted during the genesis block they will be implicitly wiped. - frame_system::Pallet::::set_block_number(1u32.into()); - queue_upward_msg::(&host_conf, para, msg.clone()); - Ump::::process_pending_upward_messages(); - assert_last_event_type::( - Event::OverweightEnqueued(para, upward_message_id(&msg), 0, Weight::zero()).into() - ); - }: _(RawOrigin::Root, 0, Weight::MAX) - verify { - assert_last_event_type::(Event::OverweightServiced(0, Weight::zero()).into()); - } -} - -frame_benchmarking::impl_benchmark_test_suite!( - Ump, - crate::mock::new_test_ext(crate::ump::tests::GenesisConfigBuilder::default().build()), - crate::mock::Test -); diff --git a/runtime/parachains/src/ump/migration.rs b/runtime/parachains/src/ump/migration.rs deleted file mode 100644 index 22d7b9adca45..000000000000 --- a/runtime/parachains/src/ump/migration.rs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -use crate::ump::{Config, Overweight, Pallet}; -use frame_support::{ - pallet_prelude::*, - traits::{OnRuntimeUpgrade, StorageVersion}, - weights::Weight, -}; - -pub mod v1 { - use super::*; - - pub struct MigrateToV1(sp_std::marker::PhantomData); - impl OnRuntimeUpgrade for MigrateToV1 { - fn on_runtime_upgrade() -> Weight { - if StorageVersion::get::>() == 0 { - let mut weight = T::DbWeight::get().reads(1); - - let overweight_messages = Overweight::::initialize_counter() as u64; - log::info!("Initialized Overweight to {}", overweight_messages); - - weight.saturating_accrue(T::DbWeight::get().reads_writes(overweight_messages, 1)); - - StorageVersion::new(1).put::>(); - - weight.saturating_add(T::DbWeight::get().writes(1)) - } else { - log::warn!("skipping v1, should be removed"); - T::DbWeight::get().reads(1) - } - } - } -} diff --git a/runtime/parachains/src/ump/tests.rs b/runtime/parachains/src/ump/tests.rs deleted file mode 100644 index 12a217927d36..000000000000 --- a/runtime/parachains/src/ump/tests.rs +++ /dev/null @@ -1,362 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -use super::*; -use crate::mock::{ - assert_last_event, new_test_ext, take_processed, Configuration, MockGenesisConfig, - RuntimeOrigin, System, Test, Ump, -}; -use frame_support::{assert_noop, assert_ok, weights::Weight}; -use std::collections::HashSet; - -pub(super) struct GenesisConfigBuilder { - max_upward_message_size: u32, - max_upward_message_num_per_candidate: u32, - max_upward_queue_count: u32, - max_upward_queue_size: u32, - ump_service_total_weight: Weight, - ump_max_individual_weight: Weight, -} - -impl Default for GenesisConfigBuilder { - fn default() -> Self { - Self { - max_upward_message_size: 32, - max_upward_message_num_per_candidate: 2, - max_upward_queue_count: 4, - max_upward_queue_size: 64, - ump_service_total_weight: Weight::from_parts(1000, 1000), - ump_max_individual_weight: Weight::from_parts(100, 100), - } - } -} - -impl GenesisConfigBuilder { - pub(super) fn build(self) -> crate::mock::MockGenesisConfig { - let mut genesis = default_genesis_config(); - let config = &mut genesis.configuration.config; - - config.max_upward_message_size = self.max_upward_message_size; - config.max_upward_message_num_per_candidate = self.max_upward_message_num_per_candidate; - config.max_upward_queue_count = self.max_upward_queue_count; - config.max_upward_queue_size = self.max_upward_queue_size; - config.ump_service_total_weight = self.ump_service_total_weight; - config.ump_max_individual_weight = self.ump_max_individual_weight; - genesis - } -} - -fn default_genesis_config() -> MockGenesisConfig { - MockGenesisConfig { - configuration: crate::configuration::GenesisConfig { - config: crate::configuration::HostConfiguration { - max_downward_message_size: 1024, - ..Default::default() - }, - }, - ..Default::default() - } -} - -fn queue_upward_msg(para: ParaId, msg: UpwardMessage) { - let msgs: UpwardMessages = vec![msg].try_into().unwrap(); - assert!(Ump::check_upward_messages(&Configuration::config(), para, &msgs).is_ok()); - let _ = Ump::receive_upward_messages(para, msgs); -} - -fn assert_storage_consistency_exhaustive() { - // check that empty queues don't clutter the storage. - for (_para, queue) in RelayDispatchQueues::::iter() { - assert!(!queue.is_empty()); - } - - // actually count the counts and sizes in queues and compare them to the bookkept version. - for (para, queue) in RelayDispatchQueues::::iter() { - let (expected_count, expected_size) = RelayDispatchQueueSize::::get(para); - let (actual_count, actual_size) = queue - .into_iter() - .fold((0, 0), |(acc_count, acc_size), x| (acc_count + 1, acc_size + x.len() as u32)); - - assert_eq!(expected_count, actual_count); - assert_eq!(expected_size, actual_size); - } - - // since we wipe the empty queues the sets of paras in queue contents, queue sizes and - // need dispatch set should all be equal. - let queue_contents_set = - RelayDispatchQueues::::iter().map(|(k, _)| k).collect::>(); - let queue_sizes_set = RelayDispatchQueueSize::::iter() - .map(|(k, _)| k) - .collect::>(); - let needs_dispatch_set = NeedsDispatch::::get().into_iter().collect::>(); - assert_eq!(queue_contents_set, queue_sizes_set); - assert_eq!(queue_contents_set, needs_dispatch_set); - - // `NextDispatchRoundStartWith` should point into a para that is tracked. - if let Some(para) = NextDispatchRoundStartWith::::get() { - assert!(queue_contents_set.contains(¶)); - } - - // `NeedsDispatch` is always sorted. - assert!(NeedsDispatch::::get().windows(2).all(|xs| xs[0] <= xs[1])); -} - -#[test] -fn dispatch_empty() { - new_test_ext(default_genesis_config()).execute_with(|| { - assert_storage_consistency_exhaustive(); - - // make sure that the case with empty queues is handled properly - Ump::process_pending_upward_messages(); - - assert_storage_consistency_exhaustive(); - }); -} - -#[test] -fn dispatch_single_message() { - let a = ParaId::from(228); - let msg = 1000u32.encode(); - - new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| { - queue_upward_msg(a, msg.clone()); - Ump::process_pending_upward_messages(); - assert_eq!(take_processed(), vec![(a, msg)]); - - assert_storage_consistency_exhaustive(); - }); -} - -#[test] -fn dispatch_resume_after_exceeding_dispatch_stage_weight() { - let a = ParaId::from(128); - let c = ParaId::from(228); - let q = ParaId::from(911); - - let a_msg_1 = (200u32, "a_msg_1").encode(); - let a_msg_2 = (100u32, "a_msg_2").encode(); - let c_msg_1 = (300u32, "c_msg_1").encode(); - let c_msg_2 = (100u32, "c_msg_2").encode(); - let q_msg = (500u32, "q_msg").encode(); - - new_test_ext( - GenesisConfigBuilder { - ump_service_total_weight: Weight::from_parts(500, 500), - ..Default::default() - } - .build(), - ) - .execute_with(|| { - queue_upward_msg(q, q_msg.clone()); - queue_upward_msg(c, c_msg_1.clone()); - queue_upward_msg(a, a_msg_1.clone()); - queue_upward_msg(a, a_msg_2.clone()); - - assert_storage_consistency_exhaustive(); - - // we expect only two first messages to fit in the first iteration. - Ump::process_pending_upward_messages(); - assert_eq!(take_processed(), vec![(a, a_msg_1), (c, c_msg_1)]); - assert_storage_consistency_exhaustive(); - - queue_upward_msg(c, c_msg_2.clone()); - assert_storage_consistency_exhaustive(); - - // second iteration should process the second message. - Ump::process_pending_upward_messages(); - assert_eq!(take_processed(), vec![(q, q_msg)]); - assert_storage_consistency_exhaustive(); - - // 3rd iteration. - Ump::process_pending_upward_messages(); - assert_eq!(take_processed(), vec![(a, a_msg_2), (c, c_msg_2)]); - assert_storage_consistency_exhaustive(); - - // finally, make sure that the queue is empty. - Ump::process_pending_upward_messages(); - assert_eq!(take_processed(), vec![]); - assert_storage_consistency_exhaustive(); - }); -} - -#[test] -fn dispatch_keeps_message_after_weight_exhausted() { - let a = ParaId::from(128); - - let a_msg_1 = (300u32, "a_msg_1").encode(); - let a_msg_2 = (300u32, "a_msg_2").encode(); - - new_test_ext( - GenesisConfigBuilder { - ump_service_total_weight: Weight::from_parts(500, 500), - ump_max_individual_weight: Weight::from_parts(300, 300), - ..Default::default() - } - .build(), - ) - .execute_with(|| { - queue_upward_msg(a, a_msg_1.clone()); - queue_upward_msg(a, a_msg_2.clone()); - - assert_storage_consistency_exhaustive(); - - // we expect only one message to fit in the first iteration. - Ump::process_pending_upward_messages(); - assert_eq!(take_processed(), vec![(a, a_msg_1)]); - assert_storage_consistency_exhaustive(); - - // second iteration should process the remaining message. - Ump::process_pending_upward_messages(); - assert_eq!(take_processed(), vec![(a, a_msg_2)]); - assert_storage_consistency_exhaustive(); - - // finally, make sure that the queue is empty. - Ump::process_pending_upward_messages(); - assert_eq!(take_processed(), vec![]); - assert_storage_consistency_exhaustive(); - }); -} - -#[test] -fn dispatch_correctly_handle_remove_of_latest() { - let a = ParaId::from(1991); - let b = ParaId::from(1999); - - let a_msg_1 = (300u32, "a_msg_1").encode(); - let a_msg_2 = (300u32, "a_msg_2").encode(); - let b_msg_1 = (300u32, "b_msg_1").encode(); - - new_test_ext( - GenesisConfigBuilder { - ump_service_total_weight: Weight::from_parts(900, 900), - ..Default::default() - } - .build(), - ) - .execute_with(|| { - // We want to test here an edge case, where we remove the queue with the highest - // para id (i.e. last in the `needs_dispatch` order). - // - // If the last entry was removed we should proceed execution, assuming we still have - // weight available. - - queue_upward_msg(a, a_msg_1.clone()); - queue_upward_msg(a, a_msg_2.clone()); - queue_upward_msg(b, b_msg_1.clone()); - Ump::process_pending_upward_messages(); - assert_eq!(take_processed(), vec![(a, a_msg_1), (b, b_msg_1), (a, a_msg_2)]); - }); -} - -#[test] -fn verify_relay_dispatch_queue_size_is_externally_accessible() { - // Make sure that the relay dispatch queue size storage entry is accessible via well known - // keys and is decodable into a (u32, u32). - - use parity_scale_codec::Decode as _; - use primitives::well_known_keys; - - let a = ParaId::from(228); - let msg = vec![1, 2, 3]; - - new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| { - queue_upward_msg(a, msg); - - let raw_queue_size = sp_io::storage::get(&well_known_keys::relay_dispatch_queue_size(a)) - .expect( - "enqueing a message should create the dispatch queue\ - and it should be accessible via the well known keys", - ); - let (cnt, size) = <(u32, u32)>::decode(&mut &raw_queue_size[..]) - .expect("the dispatch queue size should be decodable into (u32, u32)"); - - assert_eq!(cnt, 1); - assert_eq!(size, 3); - }); -} - -#[test] -fn service_overweight_unknown() { - // This test just makes sure that 0 is not a valid index and we can use it not worrying in - // the next test. - new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| { - assert_noop!( - Ump::service_overweight(RuntimeOrigin::root(), 0, Weight::from_parts(1000, 1000)), - Error::::UnknownMessageIndex - ); - }); -} - -#[test] -fn overweight_queue_works() { - let para_a = ParaId::from(2021); - - let a_msg_1 = (301u32, "a_msg_1").encode(); - let a_msg_2 = (500u32, "a_msg_2").encode(); - let a_msg_3 = (500u32, "a_msg_3").encode(); - - new_test_ext( - GenesisConfigBuilder { - ump_service_total_weight: Weight::from_parts(900, 900), - ump_max_individual_weight: Weight::from_parts(300, 300), - ..Default::default() - } - .build(), - ) - .execute_with(|| { - // HACK: Start with the block number 1. This is needed because should an event be - // emitted during the genesis block they will be implicitly wiped. - System::set_block_number(1); - - // This one is overweight. However, the weight is plenty and we can afford to execute - // this message, thus expect it. - queue_upward_msg(para_a, a_msg_1.clone()); - Ump::process_pending_upward_messages(); - assert_eq!(take_processed(), vec![(para_a, a_msg_1)]); - - // This is overweight and this message cannot fit into the total weight budget. - queue_upward_msg(para_a, a_msg_2.clone()); - queue_upward_msg(para_a, a_msg_3.clone()); - Ump::process_pending_upward_messages(); - assert_last_event( - Event::OverweightEnqueued( - para_a, - upward_message_id(&a_msg_3[..]), - 0, - Weight::from_parts(500, 500), - ) - .into(), - ); - - // Now verify that if we wanted to service this overweight message with less than enough - // weight it will fail. - assert_noop!( - Ump::service_overweight(RuntimeOrigin::root(), 0, Weight::from_parts(499, 499)), - Error::::WeightOverLimit - ); - - // ... and if we try to service it with just enough weight it will succeed as well. - assert_ok!(Ump::service_overweight(RuntimeOrigin::root(), 0, Weight::from_parts(500, 500))); - assert_last_event(Event::OverweightServiced(0, Weight::from_parts(500, 500)).into()); - - // ... and if we try to service a message with index that doesn't exist it will error - // out. - assert_noop!( - Ump::service_overweight(RuntimeOrigin::root(), 1, Weight::from_parts(1000, 1000)), - Error::::UnknownMessageIndex - ); - }); -} diff --git a/runtime/parachains/src/ump_tests.rs b/runtime/parachains/src/ump_tests.rs new file mode 100644 index 000000000000..77c822c7e640 --- /dev/null +++ b/runtime/parachains/src/ump_tests.rs @@ -0,0 +1,652 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +use crate::{ + inclusion::{ + tests::run_to_block_default_notifications as run_to_block, AggregateMessageOrigin, + AggregateMessageOrigin::Ump, UmpAcceptanceCheckErr, UmpQueueId, + }, + mock::{ + assert_last_event, assert_last_events, new_test_ext, Configuration, MessageQueue, + MessageQueueSize, MockGenesisConfig, ParaInclusion, Processed, System, Test, *, + }, +}; +use frame_support::{ + assert_noop, assert_ok, + pallet_prelude::*, + traits::{EnqueueMessage, ExecuteOverweightError, ServiceQueues}, + weights::Weight, +}; +use primitives::v4::{well_known_keys, Id as ParaId, UpwardMessage}; +use sp_core::twox_64; +use sp_runtime::traits::{Bounded, Hash}; +use sp_std::prelude::*; + +pub(super) struct GenesisConfigBuilder { + max_upward_message_size: u32, + max_upward_message_num_per_candidate: u32, + max_upward_queue_count: u32, + max_upward_queue_size: u32, +} + +impl Default for GenesisConfigBuilder { + fn default() -> Self { + Self { + max_upward_message_size: 16, + max_upward_message_num_per_candidate: 2, + max_upward_queue_count: 4, + max_upward_queue_size: 64, + } + } +} + +impl GenesisConfigBuilder { + pub(super) fn large_queue_count() -> Self { + Self { max_upward_queue_count: 128, ..Default::default() } + } + + pub(super) fn build(self) -> crate::mock::MockGenesisConfig { + let mut genesis = default_genesis_config(); + let config = &mut genesis.configuration.config; + + config.max_upward_message_size = self.max_upward_message_size; + config.max_upward_message_num_per_candidate = self.max_upward_message_num_per_candidate; + config.max_upward_queue_count = self.max_upward_queue_count; + config.max_upward_queue_size = self.max_upward_queue_size; + genesis + } +} + +fn default_genesis_config() -> MockGenesisConfig { + MockGenesisConfig { + configuration: crate::configuration::GenesisConfig { + config: crate::configuration::HostConfiguration { + max_downward_message_size: 1024, + ..Default::default() + }, + }, + ..Default::default() + } +} + +fn queue_upward_msg(para: ParaId, msg: UpwardMessage) { + try_queue_upward_msg(para, msg).unwrap(); +} + +fn try_queue_upward_msg(para: ParaId, msg: UpwardMessage) -> Result<(), UmpAcceptanceCheckErr> { + let msgs = vec![msg]; + ParaInclusion::check_upward_messages(&Configuration::config(), para, &msgs)?; + ParaInclusion::receive_upward_messages(para, msgs.as_slice()); + Ok(()) +} + +mod check_upward_messages { + use super::*; + + const P_0: ParaId = ParaId::new(0u32); + const P_1: ParaId = ParaId::new(1u32); + + // Currently its trivial since unbounded, but this function will be handy when we bound it. + fn msg(data: &str) -> UpwardMessage { + data.as_bytes().to_vec() + } + + /// Check that these messages *could* be queued. + fn check(para: ParaId, msgs: Vec, err: Option) { + assert_eq!( + ParaInclusion::check_upward_messages(&Configuration::config(), para, &msgs[..]).err(), + err + ); + } + + /// Enqueue these upward messages. + fn queue(para: ParaId, msgs: Vec) { + msgs.into_iter().for_each(|msg| super::queue_upward_msg(para, msg)); + } + + #[test] + fn basic_works() { + new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| { + let _g = frame_support::StorageNoopGuard::default(); + check(P_0, vec![msg("p0m0")], None); + check(P_1, vec![msg("p1m0")], None); + check(P_0, vec![msg("p0m1")], None); + check(P_1, vec![msg("p1m1")], None); + }); + } + + #[test] + fn num_per_candidate_exceeded_error() { + new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| { + let _g = frame_support::StorageNoopGuard::default(); + let permitted = Configuration::config().max_upward_message_num_per_candidate; + + for sent in 0..permitted + 1 { + check(P_0, vec![msg(""); sent as usize], None); + } + for sent in permitted + 1..permitted + 10 { + check( + P_0, + vec![msg(""); sent as usize], + Some(UmpAcceptanceCheckErr::MoreMessagesThanPermitted { sent, permitted }), + ); + } + }); + } + + #[test] + fn size_per_message_exceeded_error() { + new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| { + let _g = frame_support::StorageNoopGuard::default(); + let max_size = Configuration::config().max_upward_message_size; + let max_per_candidate = Configuration::config().max_upward_message_num_per_candidate; + + for msg_size in 0..=max_size { + check(P_0, vec![vec![0; msg_size as usize]], None); + } + for msg_size in max_size + 1..max_size + 10 { + for goods in 0..max_per_candidate { + let mut msgs = vec![vec![0; max_size as usize]; goods as usize]; + msgs.push(vec![0; msg_size as usize]); + + check( + P_0, + msgs, + Some(UmpAcceptanceCheckErr::MessageSize { idx: goods, msg_size, max_size }), + ); + } + } + }); + } + + #[test] + fn queue_count_exceeded_error() { + new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| { + let limit = Configuration::config().max_upward_queue_count as u64; + + for _ in 0..limit { + check(P_0, vec![msg("")], None); + queue(P_0, vec![msg("")]); + } + + check( + P_0, + vec![msg("")], + Some(UmpAcceptanceCheckErr::CapacityExceeded { count: limit + 1, limit }), + ); + check( + P_0, + vec![msg(""); 2], + Some(UmpAcceptanceCheckErr::CapacityExceeded { count: limit + 2, limit }), + ); + }); + } + + #[test] + fn queue_size_exceeded_error() { + new_test_ext(GenesisConfigBuilder::large_queue_count().build()).execute_with(|| { + let limit = Configuration::config().max_upward_queue_size as u64; + assert_eq!(pallet_message_queue::ItemHeader::::max_encoded_len(), 5); + assert!( + Configuration::config().max_upward_queue_size < + crate::inclusion::MaxUmpMessageLenOf::::get(), + "Test will not work" + ); + + for _ in 0..limit { + check(P_0, vec![msg("1")], None); + queue(P_0, vec![msg("1")]); + } + + check( + P_0, + vec![msg("1")], + Some(UmpAcceptanceCheckErr::TotalSizeExceeded { total_size: limit + 1, limit }), + ); + check( + P_0, + vec![msg("123456")], + Some(UmpAcceptanceCheckErr::TotalSizeExceeded { total_size: limit + 6, limit }), + ); + }); + } +} + +#[test] +fn dispatch_empty() { + new_test_ext(default_genesis_config()).execute_with(|| { + // make sure that the case with empty queues is handled properly + MessageQueue::service_queues(Weight::max_value()); + }); +} + +#[test] +fn dispatch_single_message() { + let a = ParaId::from(228); + let msg = 1000u32.encode(); + + new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| { + queue_upward_msg(a, msg.clone()); + MessageQueue::service_queues(Weight::max_value()); + assert_eq!(Processed::take(), vec![(a, msg)]); + }); +} + +#[test] +fn dispatch_resume_after_exceeding_dispatch_stage_weight() { + let a = ParaId::from(128); + let c = ParaId::from(228); + let q = ParaId::from(911); + + let a_msg_1 = (200u32, "a_msg_1").encode(); + let a_msg_2 = (100u32, "a_msg_2").encode(); + let c_msg_1 = (300u32, "c_msg_1").encode(); + let c_msg_2 = (100u32, "c_msg_2").encode(); + let q_msg = (500u32, "q_msg").encode(); + + new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| { + queue_upward_msg(q, q_msg.clone()); + queue_upward_msg(c, c_msg_1.clone()); + queue_upward_msg(a, a_msg_1.clone()); + queue_upward_msg(a, a_msg_2.clone()); + + // we expect only two first messages to fit in the first iteration. + MessageQueue::service_queues(Weight::from_parts(500, 500)); + assert_eq!(Processed::take(), vec![(q, q_msg)]); + queue_upward_msg(c, c_msg_2.clone()); + // second iteration should process the second message. + MessageQueue::service_queues(Weight::from_parts(500, 500)); + assert_eq!(Processed::take(), vec![(c, c_msg_1), (c, c_msg_2)]); + // 3rd iteration. + MessageQueue::service_queues(Weight::from_parts(500, 500)); + assert_eq!(Processed::take(), vec![(a, a_msg_1), (a, a_msg_2)]); + // finally, make sure that the queue is empty. + MessageQueue::service_queues(Weight::from_parts(500, 500)); + assert_eq!(Processed::take(), vec![]); + }); +} + +#[test] +fn dispatch_keeps_message_after_weight_exhausted() { + let a = ParaId::from(128); + + let a_msg_1 = (300u32, "a_msg_1").encode(); + let a_msg_2 = (300u32, "a_msg_2").encode(); + + new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| { + queue_upward_msg(a, a_msg_1.clone()); + queue_upward_msg(a, a_msg_2.clone()); + + // we expect only one message to fit in the first iteration. + MessageQueue::service_queues(Weight::from_parts(500, 500)); + assert_eq!(Processed::take(), vec![(a, a_msg_1)]); + // second iteration should process the remaining message. + MessageQueue::service_queues(Weight::from_parts(500, 500)); + assert_eq!(Processed::take(), vec![(a, a_msg_2)]); + // finally, make sure that the queue is empty. + MessageQueue::service_queues(Weight::from_parts(500, 500)); + assert_eq!(Processed::take(), vec![]); + }); +} + +#[test] +fn dispatch_correctly_handle_remove_of_latest() { + let a = ParaId::from(1991); + let b = ParaId::from(1999); + + let a_msg_1 = (300u32, "a_msg_1").encode(); + let a_msg_2 = (300u32, "a_msg_2").encode(); + let b_msg_1 = (300u32, "b_msg_1").encode(); + + new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| { + // We want to test here an edge case, where we remove the queue with the highest + // para id (i.e. last in the `needs_dispatch` order). + // + // If the last entry was removed we should proceed execution, assuming we still have + // weight available. + + queue_upward_msg(a, a_msg_1.clone()); + queue_upward_msg(a, a_msg_2.clone()); + queue_upward_msg(b, b_msg_1.clone()); + MessageQueue::service_queues(Weight::from_parts(900, 900)); + assert_eq!(Processed::take(), vec![(a, a_msg_1), (a, a_msg_2), (b, b_msg_1)]); + }); +} + +#[test] +#[cfg_attr(debug_assertions, should_panic = "Defensive failure has been triggered")] +fn queue_enact_too_long_ignored() { + const P_0: ParaId = ParaId::new(0u32); + + new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| { + let max_enact = crate::inclusion::MaxUmpMessageLenOf::::get() as usize; + let m1 = (300u32, "a_msg_1").encode(); + let m2 = vec![0u8; max_enact + 1]; + let m3 = (300u32, "a_msg_3").encode(); + + // .. but the enact defensively ignores. + ParaInclusion::receive_upward_messages(P_0, &[m1.clone(), m2.clone(), m3.clone()]); + // There is one message in the queue now: + MessageQueue::service_queues(Weight::from_parts(900, 900)); + assert_eq!(Processed::take(), vec![(P_0, m1), (P_0, m3)]); + }); +} + +/// Check that the Inclusion pallet correctly updates the well known keys in the MQ handler. +/// +/// Also checks that it works in the presence of overweight messages. +#[test] +fn relay_dispatch_queue_size_is_updated() { + new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| { + let cfg = Configuration::config(); + + for p in 0..100 { + let para = p.into(); + // Do some tricks with the weight such that the MQ pallet will process in order: + // Q0:0, Q1:0 … Q0:1, Q1:1 … + let m1 = (300u32 * (100 - p), "m1").encode(); + let m2 = (300u32 * (100 - p), "m11").encode(); + + queue_upward_msg(para, m1); + queue_upward_msg(para, m2); + + assert_queue_size(para, 2, 15); + assert_queue_remaining( + para, + cfg.max_upward_queue_count - 2, + cfg.max_upward_queue_size - 15, + ); + + // Now processing one message should also update the queue size. + MessageQueue::service_queues(Weight::from_all(300u64 * (100 - p) as u64)); + assert_queue_remaining( + para, + cfg.max_upward_queue_count - 1, + cfg.max_upward_queue_size - 8, + ); + } + + // The messages of Q0…Q98 are overweight, so `service_queues` wont help. + for p in 0..98 { + let para = UmpQueueId::Para(p.into()); + MessageQueue::service_queues(Weight::from_all(u64::MAX)); + + let fp = MessageQueue::footprint(AggregateMessageOrigin::Ump(para)); + let (para_queue_count, para_queue_size) = (fp.count, fp.size); + assert_eq!(para_queue_count, 1, "count wrong for para: {}", p); + assert_eq!(para_queue_size, 8, "size wrong for para: {}", p); + } + // All queues are empty after processing overweight messages. + for p in 0..100 { + let para = UmpQueueId::Para(p.into()); + let _ = ::execute_overweight( + Weight::from_all(u64::MAX), + (AggregateMessageOrigin::Ump(para.clone()), 0, 1), + ); + + assert_queue_remaining(p.into(), cfg.max_upward_queue_count, cfg.max_upward_queue_size); + let fp = MessageQueue::footprint(AggregateMessageOrigin::Ump(para)); + let (para_queue_count, para_queue_size) = (fp.count, fp.size); + assert_eq!(para_queue_count, 0, "count wrong for para: {}", p); + assert_eq!(para_queue_size, 0, "size wrong for para: {}", p); + } + }); +} + +/// Assert that the old and the new way of accessing `relay_dispatch_queue_size` is the same. +#[test] +fn relay_dispatch_queue_size_key_is_correct() { + #![allow(deprecated)] + // Storage alias to the old way of accessing the queue size. + #[frame_support::storage_alias] + type RelayDispatchQueueSize = StorageMap; + + for i in 0..1024 { + // A "random" para id. + let para: ParaId = u32::from_ne_bytes(twox_64(&i.encode())[..4].try_into().unwrap()).into(); + + let well_known = primitives::well_known_keys::relay_dispatch_queue_size(para); + let aliased = RelayDispatchQueueSize::hashed_key_for(para); + + assert_eq!(well_known, aliased, "Old and new key must match"); + } +} + +#[test] +fn verify_relay_dispatch_queue_size_is_externally_accessible() { + // Make sure that the relay dispatch queue size storage entry is accessible via well known + // keys and is decodable into a (u32, u32). + new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| { + let cfg = Configuration::config(); + + for para in 0..10 { + let para = para.into(); + queue_upward_msg(para, vec![0u8; 3]); + assert_queue_size(para, 1, 3); + assert_queue_remaining( + para, + cfg.max_upward_queue_count - 1, + cfg.max_upward_queue_size - 3, + ); + + queue_upward_msg(para, vec![0u8; 3]); + assert_queue_size(para, 2, 6); + assert_queue_remaining( + para, + cfg.max_upward_queue_count - 2, + cfg.max_upward_queue_size - 6, + ); + } + }); +} + +fn assert_queue_size(para: ParaId, count: u32, size: u32) { + #[allow(deprecated)] + let raw_queue_size = sp_io::storage::get(&well_known_keys::relay_dispatch_queue_size(para)).expect( + "enqueing a message should create the dispatch queue\ + and it should be accessible via the well known keys", + ); + let (c, s) = <(u32, u32)>::decode(&mut &raw_queue_size[..]) + .expect("the dispatch queue size should be decodable into (u32, u32)"); + assert_eq!((c, s), (count, size)); + + // Test the deprecated but at least type-safe `relay_dispatch_queue_size_typed`: + #[allow(deprecated)] + let (c, s) = well_known_keys::relay_dispatch_queue_size_typed(para).get().expect( + "enqueing a message should create the dispatch queue\ + and it should be accessible via the well known keys", + ); + assert_eq!((c, s), (count, size)); +} + +fn assert_queue_remaining(para: ParaId, count: u32, size: u32) { + let (remaining_cnt, remaining_size) = + well_known_keys::relay_dispatch_queue_remaining_capacity(para) + .get() + .expect("No storage value"); + assert_eq!(remaining_cnt, count, "Wrong number of remaining messages in Q{}", para); + assert_eq!(remaining_size, size, "Wrong remaining size in Q{}", para); +} + +#[test] +fn service_overweight_unknown() { + // This test just makes sure that 0 is not a valid index and we can use it not worrying in + // the next test. + new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| { + assert_noop!( + ::execute_overweight( + Weight::MAX, + (Ump(UmpQueueId::Para(0u32.into())), 0, 0) + ), + ExecuteOverweightError::NotFound, + ); + }); +} + +#[test] +fn overweight_queue_works() { + let para_a = ParaId::from(2021); + + let a_msg_1 = (301u32, "a_msg_1").encode(); + let a_msg_2 = (501u32, "a_msg_2").encode(); + let a_msg_3 = (501u32, "a_msg_3").encode(); + + new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| { + // HACK: Start with the block number 1. This is needed because should an event be + // emitted during the genesis block they will be implicitly wiped. + System::set_block_number(1); + + // This one is overweight. However, the weight is plenty and we can afford to execute + // this message, thus expect it. + queue_upward_msg(para_a, a_msg_1.clone()); + queue_upward_msg(para_a, a_msg_2.clone()); + queue_upward_msg(para_a, a_msg_3.clone()); + + MessageQueue::service_queues(Weight::from_parts(500, 500)); + let hash_1 = <::Hashing as Hash>::hash(&a_msg_1[..]); + let hash_2 = <::Hashing as Hash>::hash(&a_msg_2[..]); + let hash_3 = <::Hashing as Hash>::hash(&a_msg_3[..]); + assert_last_events( + [ + pallet_message_queue::Event::::Processed { + hash: hash_1.clone(), + origin: Ump(UmpQueueId::Para(para_a)), + weight_used: Weight::from_parts(301, 301), + success: true, + } + .into(), + pallet_message_queue::Event::::OverweightEnqueued { + hash: hash_2.clone(), + origin: Ump(UmpQueueId::Para(para_a)), + page_index: 0, + message_index: 1, + } + .into(), + pallet_message_queue::Event::::OverweightEnqueued { + hash: hash_3.clone(), + origin: Ump(UmpQueueId::Para(para_a)), + page_index: 0, + message_index: 2, + } + .into(), + ] + .into_iter(), + ); + assert_eq!(Processed::take(), vec![(para_a, a_msg_1)]); + + // Now verify that if we wanted to service this overweight message with less than enough + // weight it will fail. + assert_noop!( + ::execute_overweight( + Weight::from_parts(500, 500), + (Ump(UmpQueueId::Para(para_a)), 0, 2) + ), + ExecuteOverweightError::InsufficientWeight, + ); + + // ... and if we try to service it with just enough weight it will succeed as well. + assert_ok!(::execute_overweight( + Weight::from_parts(501, 501), + (Ump(UmpQueueId::Para(para_a)), 0, 2) + )); + assert_last_event( + pallet_message_queue::Event::::Processed { + hash: hash_3, + origin: Ump(UmpQueueId::Para(para_a)), + weight_used: Weight::from_parts(501, 501), + success: true, + } + .into(), + ); + + // ... and if we try to service a message with index that doesn't exist it will error + // out. + assert_noop!( + ::execute_overweight( + Weight::from_parts(501, 501), + (Ump(UmpQueueId::Para(para_a)), 0, 2) + ), + ExecuteOverweightError::NotFound, + ); + }); +} + +/// Tests that UMP messages in the dispatch queue of the relay prevents the parachain from being +/// scheduled for offboarding. +#[test] +fn cannot_offboard_while_ump_dispatch_queued() { + let para = 32.into(); + let msg = (300u32, "something").encode(); + + new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| { + register_parachain(para); + run_to_block(5, vec![4, 5]); + + queue_upward_msg(para, msg.clone()); + queue_upward_msg(para, msg.clone()); + // Cannot offboard since there are two UMP messages in the queue. + for i in 6..10 { + assert!(try_deregister_parachain(para).is_err()); + run_to_block(i, vec![i]); + assert!(Paras::is_valid_para(para)); + } + + // Now let's process the first message. + MessageQueue::on_initialize(System::block_number()); + assert_eq!(Processed::take().len(), 1); + // Cannot offboard since there is another one in the queue. + assert!(try_deregister_parachain(para).is_err()); + // Now also process the second message ... + MessageQueue::on_initialize(System::block_number()); + assert_eq!(Processed::take().len(), 1); + + // ... and offboard. + run_to_block(10, vec![10]); + assert!(Paras::is_valid_para(para)); + assert_ok!(try_deregister_parachain(para)); + assert!(Paras::is_offboarding(para)); + + // Offboarding completed. + run_to_block(11, vec![11]); + assert!(!Paras::is_valid_para(para)); + }); +} + +/// A para-chain cannot send an UMP to the relay chain while it is offboarding. +#[test] +fn cannot_enqueue_ump_while_offboarding() { + let para = 32.into(); + let msg = (300u32, "something").encode(); + + new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| { + register_parachain(para); + run_to_block(5, vec![4, 5]); + + // Start with an offboarding para. + assert_ok!(try_deregister_parachain(para)); + assert!(Paras::is_offboarding(para)); + + // Cannot enqueue a message. + assert!(try_queue_upward_msg(para, msg.clone()).is_err()); + run_to_block(6, vec![6]); + // Para is still there and still cannot enqueue a message. + assert!(Paras::is_offboarding(para)); + assert!(try_queue_upward_msg(para, msg.clone()).is_err()); + // Now offboarding is completed. + run_to_block(7, vec![7]); + assert!(!Paras::is_valid_para(para)); + }); +} diff --git a/runtime/polkadot/Cargo.toml b/runtime/polkadot/Cargo.toml index 56f835ea5a47..5e59c22c984d 100644 --- a/runtime/polkadot/Cargo.toml +++ b/runtime/polkadot/Cargo.toml @@ -56,6 +56,7 @@ pallet-identity = { git = "https://github.com/paritytech/substrate", branch = "m pallet-im-online = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-indices = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-membership = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-message-queue = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-multisig = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-nomination-pools = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-nomination-pools-runtime-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -154,6 +155,7 @@ std = [ "pallet-im-online/std", "pallet-indices/std", "pallet-membership/std", + "pallet-message-queue/std", "pallet-multisig/std", "pallet-nomination-pools/std", "pallet-nomination-pools-runtime-api/std", @@ -214,6 +216,7 @@ runtime-benchmarks = [ "pallet-im-online/runtime-benchmarks", "pallet-indices/runtime-benchmarks", "pallet-membership/runtime-benchmarks", + "pallet-message-queue/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", "pallet-nomination-pools/runtime-benchmarks", "pallet-nomination-pools-benchmarking/runtime-benchmarks", @@ -262,6 +265,7 @@ try-runtime = [ "pallet-im-online/try-runtime", "pallet-indices/try-runtime", "pallet-membership/try-runtime", + "pallet-message-queue/try-runtime", "pallet-multisig/try-runtime", "pallet-nomination-pools/try-runtime", "pallet-offences/try-runtime", diff --git a/runtime/polkadot/src/lib.rs b/runtime/polkadot/src/lib.rs index 8968308c0ed6..27bc0bc48675 100644 --- a/runtime/polkadot/src/lib.rs +++ b/runtime/polkadot/src/lib.rs @@ -28,12 +28,14 @@ use runtime_common::{ use runtime_parachains::{ configuration as parachains_configuration, disputes as parachains_disputes, - disputes::slashing as parachains_slashing, dmp as parachains_dmp, hrmp as parachains_hrmp, - inclusion as parachains_inclusion, initializer as parachains_initializer, - origin as parachains_origin, paras as parachains_paras, + disputes::slashing as parachains_slashing, + dmp as parachains_dmp, hrmp as parachains_hrmp, inclusion as parachains_inclusion, + inclusion::{AggregateMessageOrigin, UmpQueueId}, + initializer as parachains_initializer, origin as parachains_origin, paras as parachains_paras, paras_inherent as parachains_paras_inherent, reward_points as parachains_reward_points, - runtime_api_impl::v4 as parachains_runtime_api_impl, scheduler as parachains_scheduler, - session_info as parachains_session_info, shared as parachains_shared, ump as parachains_ump, + runtime_api_impl::v4 as parachains_runtime_api_impl, + scheduler as parachains_scheduler, session_info as parachains_session_info, + shared as parachains_shared, }; use authority_discovery_primitives::AuthorityId as AuthorityDiscoveryId; @@ -43,9 +45,9 @@ use frame_support::{ construct_runtime, parameter_types, traits::{ ConstU32, EitherOf, EitherOfDiverse, InstanceFilter, KeyOwnerProofSystem, LockIdentifier, - PrivilegeCmp, WithdrawReasons, + PrivilegeCmp, ProcessMessage, ProcessMessageError, WithdrawReasons, }, - weights::ConstantMultiplier, + weights::{ConstantMultiplier, WeightMeter}, PalletId, RuntimeDebug, }; use frame_system::EnsureRoot; @@ -80,6 +82,7 @@ use sp_std::{cmp::Ordering, collections::btree_map::BTreeMap, prelude::*}; use sp_version::NativeVersion; use sp_version::RuntimeVersion; use static_assertions::const_assert; +use xcm::latest::Junction; pub use frame_system::Call as SystemCall; pub use pallet_balances::Call as BalancesCall; @@ -1098,6 +1101,8 @@ impl parachains_inclusion::Config for Runtime { type RuntimeEvent = RuntimeEvent; type DisputesHandler = ParasDisputes; type RewardValidators = parachains_reward_points::RewardValidatorsWithEraPoints; + type MessageQueue = MessageQueue; + type WeightInfo = weights::runtime_parachains_inclusion::WeightInfo; } parameter_types! { @@ -1108,20 +1113,55 @@ impl parachains_paras::Config for Runtime { type RuntimeEvent = RuntimeEvent; type WeightInfo = weights::runtime_parachains_paras::WeightInfo; type UnsignedPriority = ParasUnsignedPriority; + type QueueFootprinter = ParaInclusion; type NextSessionRotation = Babe; } parameter_types! { - pub const FirstMessageFactorPercent: u64 = 100; + /// Amount of weight that can be spent per block to service messages. + /// + /// # WARNING + /// + /// This is not a good value for para-chains since the `Scheduler` already uses up to 80% block weight. + pub MessageQueueServiceWeight: Weight = Perbill::from_percent(20) * BlockWeights::get().max_block; + pub const MessageQueueHeapSize: u32 = 65_536; + pub const MessageQueueMaxStale: u32 = 8; +} + +/// Message processor to handle any messages that were enqueued into the `MessageQueue` pallet. +pub struct MessageProcessor; +impl ProcessMessage for MessageProcessor { + type Origin = AggregateMessageOrigin; + + fn process_message( + message: &[u8], + origin: Self::Origin, + meter: &mut WeightMeter, + ) -> Result { + let para = match origin { + AggregateMessageOrigin::Ump(UmpQueueId::Para(para)) => para, + }; + xcm_builder::ProcessXcmMessage::< + Junction, + xcm_executor::XcmExecutor, + RuntimeCall, + >::process_message(message, Junction::Parachain(para.into()), meter) + } } -impl parachains_ump::Config for Runtime { +impl pallet_message_queue::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type UmpSink = - crate::parachains_ump::XcmSink, Runtime>; - type FirstMessageFactorPercent = FirstMessageFactorPercent; - type ExecuteOverweightOrigin = EnsureRoot; - type WeightInfo = weights::runtime_parachains_ump::WeightInfo; + type Size = u32; + type HeapSize = MessageQueueHeapSize; + type MaxStale = MessageQueueMaxStale; + type ServiceWeight = MessageQueueServiceWeight; + #[cfg(not(feature = "runtime-benchmarks"))] + type MessageProcessor = MessageProcessor; + #[cfg(feature = "runtime-benchmarks")] + type MessageProcessor = + pallet_message_queue::mock_helpers::NoopMessageProcessor; + type QueueChangeHandler = ParaInclusion; + type WeightInfo = weights::pallet_message_queue::WeightInfo; } impl parachains_dmp::Config for Runtime {} @@ -1386,7 +1426,7 @@ construct_runtime! { Paras: parachains_paras::{Pallet, Call, Storage, Event, Config, ValidateUnsigned} = 56, Initializer: parachains_initializer::{Pallet, Call, Storage} = 57, Dmp: parachains_dmp::{Pallet, Storage} = 58, - Ump: parachains_ump::{Pallet, Call, Storage, Event} = 59, + // Ump 59 Hrmp: parachains_hrmp::{Pallet, Call, Storage, Event, Config} = 60, ParaSessionInfo: parachains_session_info::{Pallet, Storage} = 61, ParasDisputes: parachains_disputes::{Pallet, Call, Storage, Event} = 62, @@ -1400,6 +1440,9 @@ construct_runtime! { // Pallet for sending XCM. XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event, Origin, Config} = 99, + + // Generalized message queue + MessageQueue: pallet_message_queue::{Pallet, Call, Storage, Event} = 100, } } @@ -1453,7 +1496,8 @@ pub mod migrations { pub type V0938 = ( pallet_xcm::migration::v1::MigrateToV1, - parachains_ump::migration::v1::MigrateToV1, + // The UMP pallet got deleted in + // parachains_ump::migration::v1::MigrateToV1, ); pub type V0940 = ( @@ -1471,7 +1515,12 @@ pub mod migrations { ); /// Unreleased migrations. Add new ones here: - pub type Unreleased = SetStorageVersions; + pub type Unreleased = ( + SetStorageVersions, + // Remove UMP dispatch queue + parachains_configuration::migration::v6::MigrateToV6, + ump_migrations::UpdateUmpLimits, + ); /// Migrations that set `StorageVersion`s we missed to set. pub struct SetStorageVersions; @@ -1496,6 +1545,24 @@ pub mod migrations { } } +/// Helpers to configure all migrations. +pub mod ump_migrations { + use runtime_parachains::configuration::migration_ump; + + pub const MAX_UPWARD_QUEUE_SIZE: u32 = 1 * 1024 * 1024; + pub const MAX_UPWARD_QUEUE_COUNT: u32 = 174762; + pub const MAX_UPWARD_MESSAGE_SIZE: u32 = (1 << 16) - 5; // Checked in test `max_upward_message_size`. + pub const MAX_UPWARD_MESSAGE_NUM_PER_CANDIDATE: u32 = 16; + + pub type UpdateUmpLimits = migration_ump::latest::ScheduleConfigUpdate< + super::Runtime, + MAX_UPWARD_QUEUE_SIZE, + MAX_UPWARD_QUEUE_COUNT, + MAX_UPWARD_MESSAGE_SIZE, + MAX_UPWARD_MESSAGE_NUM_PER_CANDIDATE, + >; +} + /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; @@ -1512,13 +1579,9 @@ pub type Executive = frame_executive::Executive< /// The payload being signed in transactions. pub type SignedPayload = generic::SignedPayload; -#[cfg(feature = "runtime-benchmarks")] -#[macro_use] -extern crate frame_benchmarking; - #[cfg(feature = "runtime-benchmarks")] mod benches { - define_benchmarks!( + frame_benchmarking::define_benchmarks!( // Polkadot // NOTE: Make sure to prefix these with `runtime_common::` so // the that path resolves correctly in the generated file. @@ -1531,10 +1594,10 @@ mod benches { [runtime_parachains::disputes, ParasDisputes] [runtime_parachains::disputes::slashing, ParasSlashing] [runtime_parachains::hrmp, Hrmp] + [runtime_parachains::inclusion, ParaInclusion] [runtime_parachains::initializer, Initializer] [runtime_parachains::paras, Paras] [runtime_parachains::paras_inherent, ParaInherent] - [runtime_parachains::ump, Ump] // Substrate [pallet_bags_list, VoterList] [pallet_balances, Balances] @@ -1552,6 +1615,7 @@ mod benches { [pallet_im_online, ImOnline] [pallet_indices, Indices] [pallet_membership, TechnicalMembership] + [pallet_message_queue, MessageQueue] [pallet_multisig, Multisig] [pallet_nomination_pools, NominationPoolsBench::] [pallet_offences, OffencesBench::] @@ -2258,6 +2322,14 @@ mod test { If the limit is too strong, maybe consider increase the limit", ); } + + #[test] + fn max_upward_message_size() { + assert_eq!( + ump_migrations::MAX_UPWARD_MESSAGE_SIZE, + pallet_message_queue::MaxMessageLenOf::::get() + ); + } } #[cfg(test)] diff --git a/runtime/polkadot/src/weights/mod.rs b/runtime/polkadot/src/weights/mod.rs index 56352e7881b3..09de2dc74305 100644 --- a/runtime/polkadot/src/weights/mod.rs +++ b/runtime/polkadot/src/weights/mod.rs @@ -32,6 +32,7 @@ pub mod pallet_identity; pub mod pallet_im_online; pub mod pallet_indices; pub mod pallet_membership; +pub mod pallet_message_queue; pub mod pallet_multisig; pub mod pallet_nomination_pools; pub mod pallet_preimage; @@ -56,7 +57,7 @@ pub mod runtime_parachains_configuration; pub mod runtime_parachains_disputes; pub mod runtime_parachains_disputes_slashing; pub mod runtime_parachains_hrmp; +pub mod runtime_parachains_inclusion; pub mod runtime_parachains_initializer; pub mod runtime_parachains_paras; pub mod runtime_parachains_paras_inherent; -pub mod runtime_parachains_ump; diff --git a/runtime/polkadot/src/weights/pallet_message_queue.rs b/runtime/polkadot/src/weights/pallet_message_queue.rs new file mode 100644 index 000000000000..6800b2294406 --- /dev/null +++ b/runtime/polkadot/src/weights/pallet_message_queue.rs @@ -0,0 +1,176 @@ +// Copyright 2017-2022 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . +//! Autogenerated weights for `pallet_message_queue` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-02-28, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `i9`, CPU: `13th Gen Intel(R) Core(TM) i9-13900K` +//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot +// benchmark +// pallet +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet-message-queue +// --extrinsic=* +// --heap-pages=4096 +// --header=file_header.txt +// --output +// runtime/polkadot/src/weights/pallet_message_queue.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for `pallet_message_queue`. +pub struct WeightInfo(PhantomData); +impl pallet_message_queue::WeightInfo for WeightInfo { + /// Storage: MessageQueue ServiceHead (r:1 w:0) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:2 w:2) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + fn ready_ring_knit() -> Weight { + // Proof Size summary in bytes: + // Measured: `837` + // Estimated: `5554` + // Minimum execution time: 5_631 nanoseconds. + Weight::from_parts(6_182_000, 0) + .saturating_add(Weight::from_parts(0, 5554)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: MessageQueue BookStateFor (r:2 w:2) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue ServiceHead (r:1 w:1) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + fn ready_ring_unknit() -> Weight { + // Proof Size summary in bytes: + // Measured: `837` + // Estimated: `5554` + // Minimum execution time: 5_515 nanoseconds. + Weight::from_parts(5_775_000, 0) + .saturating_add(Weight::from_parts(0, 5554)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + fn service_queue_base() -> Weight { + // Proof Size summary in bytes: + // Measured: `576` + // Estimated: `2527` + // Minimum execution time: 2_098 nanoseconds. + Weight::from_parts(2_265_000, 0) + .saturating_add(Weight::from_parts(0, 2527)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: MessageQueue Pages (r:1 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn service_page_base_completion() -> Weight { + // Proof Size summary in bytes: + // Measured: `648` + // Estimated: `68060` + // Minimum execution time: 3_194 nanoseconds. + Weight::from_parts(3_436_000, 0) + .saturating_add(Weight::from_parts(0, 68060)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: MessageQueue Pages (r:1 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn service_page_base_no_completion() -> Weight { + // Proof Size summary in bytes: + // Measured: `648` + // Estimated: `68060` + // Minimum execution time: 3_409 nanoseconds. + Weight::from_parts(3_673_000, 0) + .saturating_add(Weight::from_parts(0, 68060)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + fn service_page_item() -> Weight { + // Proof Size summary in bytes: + // Measured: `971` + // Estimated: `0` + // Minimum execution time: 49_243 nanoseconds. + Weight::from_parts(50_380_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: MessageQueue ServiceHead (r:1 w:1) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:1 w:0) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + fn bump_service_head() -> Weight { + // Proof Size summary in bytes: + // Measured: `712` + // Estimated: `3027` + // Minimum execution time: 3_744 nanoseconds. + Weight::from_parts(3_922_000, 0) + .saturating_add(Weight::from_parts(0, 3027)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:1 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn reap_page() -> Weight { + // Proof Size summary in bytes: + // Measured: `66859` + // Estimated: `70587` + // Minimum execution time: 28_995 nanoseconds. + Weight::from_parts(30_370_000, 0) + .saturating_add(Weight::from_parts(0, 70587)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:1 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn execute_overweight_page_removed() -> Weight { + // Proof Size summary in bytes: + // Measured: `66859` + // Estimated: `70587` + // Minimum execution time: 76_268 nanoseconds. + Weight::from_parts(77_933_000, 0) + .saturating_add(Weight::from_parts(0, 70587)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:1 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn execute_overweight_page_updated() -> Weight { + // Proof Size summary in bytes: + // Measured: `66859` + // Estimated: `70587` + // Minimum execution time: 78_029 nanoseconds. + Weight::from_parts(80_632_000, 0) + .saturating_add(Weight::from_parts(0, 70587)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } +} diff --git a/runtime/polkadot/src/weights/runtime_parachains_inclusion.rs b/runtime/polkadot/src/weights/runtime_parachains_inclusion.rs new file mode 100644 index 000000000000..0ab5c1e30650 --- /dev/null +++ b/runtime/polkadot/src/weights/runtime_parachains_inclusion.rs @@ -0,0 +1,66 @@ +// Copyright 2017-2022 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . +//! Autogenerated weights for `runtime_parachains::inclusion` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-02-23, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `i9`, CPU: `13th Gen Intel(R) Core(TM) i9-13900K` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("polkadot-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot +// benchmark +// pallet +// --chain=polkadot-dev +// --steps=50 +// --repeat=20 +// --pallet=runtime_parachains::inclusion +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --header=./file_header.txt +// --output=./runtime/polkadot/src/weights + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for `runtime_parachains::inclusion`. +pub struct WeightInfo(PhantomData); +impl runtime_parachains::inclusion::WeightInfo for WeightInfo { + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:1 w:999) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + /// The range of component `i` is `[1, 1000]`. + fn receive_upward_messages(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `51490` + // Estimated: `70587` + // Minimum execution time: 50_423 nanoseconds. + Weight::from_parts(160_584_092, 0) + .saturating_add(Weight::from_parts(0, 70587)) + // Standard Error: 75_127 + .saturating_add(Weight::from_parts(41_929_458, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) + } +} diff --git a/runtime/polkadot/src/weights/runtime_parachains_ump.rs b/runtime/polkadot/src/weights/runtime_parachains_ump.rs deleted file mode 100644 index 9691abc1d049..000000000000 --- a/runtime/polkadot/src/weights/runtime_parachains_ump.rs +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -//! Autogenerated weights for `runtime_parachains::ump` -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-04-28, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm5`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("polkadot-dev"), DB CACHE: 1024 - -// Executed Command: -// ./target/production/polkadot -// benchmark -// pallet -// --chain=polkadot-dev -// --steps=50 -// --repeat=20 -// --pallet=runtime_parachains::ump -// --extrinsic=* -// --execution=wasm -// --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/polkadot/src/weights/runtime_parachains_ump.rs - -#![cfg_attr(rustfmt, rustfmt_skip)] -#![allow(unused_parens)] -#![allow(unused_imports)] -#![allow(missing_docs)] - -use frame_support::{traits::Get, weights::Weight}; -use core::marker::PhantomData; - -/// Weight functions for `runtime_parachains::ump`. -pub struct WeightInfo(PhantomData); -impl runtime_parachains::ump::WeightInfo for WeightInfo { - /// The range of component `s` is `[0, 51200]`. - fn process_upward_message(s: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 5_941_000 picoseconds. - Weight::from_parts(6_024_000, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 8 - .saturating_add(Weight::from_parts(1_365, 0).saturating_mul(s.into())) - } - /// Storage: Ump NeedsDispatch (r:1 w:1) - /// Proof Skipped: Ump NeedsDispatch (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Ump NextDispatchRoundStartWith (r:1 w:1) - /// Proof Skipped: Ump NextDispatchRoundStartWith (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Ump RelayDispatchQueues (r:0 w:1) - /// Proof Skipped: Ump RelayDispatchQueues (max_values: None, max_size: None, mode: Measured) - /// Storage: Ump RelayDispatchQueueSize (r:0 w:1) - /// Proof Skipped: Ump RelayDispatchQueueSize (max_values: None, max_size: None, mode: Measured) - fn clean_ump_after_outgoing() -> Weight { - // Proof Size summary in bytes: - // Measured: `240` - // Estimated: `1725` - // Minimum execution time: 9_387_000 picoseconds. - Weight::from_parts(9_538_000, 0) - .saturating_add(Weight::from_parts(0, 1725)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(4)) - } - /// Storage: Ump Overweight (r:1 w:1) - /// Proof Skipped: Ump Overweight (max_values: None, max_size: None, mode: Measured) - /// Storage: Ump CounterForOverweight (r:1 w:1) - /// Proof: Ump CounterForOverweight (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - fn service_overweight() -> Weight { - // Proof Size summary in bytes: - // Measured: `257` - // Estimated: `3722` - // Minimum execution time: 23_382_000 picoseconds. - Weight::from_parts(23_808_000, 0) - .saturating_add(Weight::from_parts(0, 3722)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(2)) - } -} diff --git a/runtime/rococo/Cargo.toml b/runtime/rococo/Cargo.toml index af92428207a8..a6d00060d939 100644 --- a/runtime/rococo/Cargo.toml +++ b/runtime/rococo/Cargo.toml @@ -53,6 +53,7 @@ pallet-identity = { git = "https://github.com/paritytech/substrate", branch = "m pallet-im-online = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-indices = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-membership = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-message-queue = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-mmr = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-multisig = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-nis = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -140,6 +141,7 @@ std = [ "pallet-im-online/std", "pallet-indices/std", "pallet-membership/std", + "pallet-message-queue/std", "pallet-mmr/std", "pallet-multisig/std", "pallet-offences/std", @@ -199,6 +201,7 @@ runtime-benchmarks = [ "pallet-im-online/runtime-benchmarks", "pallet-indices/runtime-benchmarks", "pallet-membership/runtime-benchmarks", + "pallet-message-queue/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", "pallet-preimage/runtime-benchmarks", "pallet-proxy/runtime-benchmarks", @@ -239,6 +242,7 @@ try-runtime = [ "pallet-im-online/try-runtime", "pallet-indices/try-runtime", "pallet-membership/try-runtime", + "pallet-message-queue/try-runtime", "pallet-mmr/try-runtime", "pallet-multisig/try-runtime", "pallet-nis/try-runtime", diff --git a/runtime/rococo/src/lib.rs b/runtime/rococo/src/lib.rs index 0f2c63e22a6e..44996957f37c 100644 --- a/runtime/rococo/src/lib.rs +++ b/runtime/rococo/src/lib.rs @@ -39,12 +39,14 @@ use sp_std::{cmp::Ordering, collections::btree_map::BTreeMap, prelude::*}; use runtime_parachains::{ configuration as parachains_configuration, disputes as parachains_disputes, - disputes::slashing as parachains_slashing, dmp as parachains_dmp, hrmp as parachains_hrmp, - inclusion as parachains_inclusion, initializer as parachains_initializer, - origin as parachains_origin, paras as parachains_paras, + disputes::slashing as parachains_slashing, + dmp as parachains_dmp, hrmp as parachains_hrmp, inclusion as parachains_inclusion, + inclusion::{AggregateMessageOrigin, UmpQueueId}, + initializer as parachains_initializer, origin as parachains_origin, paras as parachains_paras, paras_inherent as parachains_paras_inherent, - runtime_api_impl::v4 as parachains_runtime_api_impl, scheduler as parachains_scheduler, - session_info as parachains_session_info, shared as parachains_shared, ump as parachains_ump, + runtime_api_impl::v4 as parachains_runtime_api_impl, + scheduler as parachains_scheduler, session_info as parachains_session_info, + shared as parachains_shared, }; use authority_discovery_primitives::AuthorityId as AuthorityDiscoveryId; @@ -57,9 +59,9 @@ use frame_support::{ construct_runtime, parameter_types, traits::{ Contains, EitherOfDiverse, InstanceFilter, KeyOwnerProofSystem, LockIdentifier, - PrivilegeCmp, StorageMapShim, WithdrawReasons, + PrivilegeCmp, ProcessMessage, ProcessMessageError, StorageMapShim, WithdrawReasons, }, - weights::ConstantMultiplier, + weights::{ConstantMultiplier, WeightMeter}, PalletId, RuntimeDebug, }; use frame_system::EnsureRoot; @@ -83,6 +85,7 @@ use sp_staking::SessionIndex; use sp_version::NativeVersion; use sp_version::RuntimeVersion; use static_assertions::const_assert; +use xcm::latest::Junction; pub use frame_system::Call as SystemCall; pub use pallet_balances::Call as BalancesCall; @@ -1025,6 +1028,8 @@ impl parachains_inclusion::Config for Runtime { type RuntimeEvent = RuntimeEvent; type DisputesHandler = ParasDisputes; type RewardValidators = RewardValidators; + type MessageQueue = MessageQueue; + type WeightInfo = weights::runtime_parachains_inclusion::WeightInfo; } parameter_types! { @@ -1035,20 +1040,55 @@ impl parachains_paras::Config for Runtime { type RuntimeEvent = RuntimeEvent; type WeightInfo = weights::runtime_parachains_paras::WeightInfo; type UnsignedPriority = ParasUnsignedPriority; + type QueueFootprinter = ParaInclusion; type NextSessionRotation = Babe; } parameter_types! { - pub const FirstMessageFactorPercent: u64 = 100; + /// Amount of weight that can be spent per block to service messages. + /// + /// # WARNING + /// + /// This is not a good value for para-chains since the `Scheduler` already uses up to 80% block weight. + pub MessageQueueServiceWeight: Weight = Perbill::from_percent(20) * BlockWeights::get().max_block; + pub const MessageQueueHeapSize: u32 = 32 * 1024; + pub const MessageQueueMaxStale: u32 = 96; +} + +/// Message processor to handle any messages that were enqueued into the `MessageQueue` pallet. +pub struct MessageProcessor; +impl ProcessMessage for MessageProcessor { + type Origin = AggregateMessageOrigin; + + fn process_message( + message: &[u8], + origin: Self::Origin, + meter: &mut WeightMeter, + ) -> Result { + let para = match origin { + AggregateMessageOrigin::Ump(UmpQueueId::Para(para)) => para, + }; + xcm_builder::ProcessXcmMessage::< + Junction, + xcm_executor::XcmExecutor, + RuntimeCall, + >::process_message(message, Junction::Parachain(para.into()), meter) + } } -impl parachains_ump::Config for Runtime { +impl pallet_message_queue::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type UmpSink = - crate::parachains_ump::XcmSink, Runtime>; - type FirstMessageFactorPercent = FirstMessageFactorPercent; - type ExecuteOverweightOrigin = EnsureRoot; - type WeightInfo = weights::runtime_parachains_ump::WeightInfo; + type Size = u32; + type HeapSize = MessageQueueHeapSize; + type MaxStale = MessageQueueMaxStale; + type ServiceWeight = MessageQueueServiceWeight; + #[cfg(not(feature = "runtime-benchmarks"))] + type MessageProcessor = MessageProcessor; + #[cfg(feature = "runtime-benchmarks")] + type MessageProcessor = + pallet_message_queue::mock_helpers::NoopMessageProcessor; + type QueueChangeHandler = ParaInclusion; + type WeightInfo = weights::pallet_message_queue::WeightInfo; } impl parachains_dmp::Config for Runtime {} @@ -1414,11 +1454,11 @@ construct_runtime! { Paras: parachains_paras::{Pallet, Call, Storage, Event, Config, ValidateUnsigned} = 56, Initializer: parachains_initializer::{Pallet, Call, Storage} = 57, Dmp: parachains_dmp::{Pallet, Storage} = 58, - Ump: parachains_ump::{Pallet, Call, Storage, Event} = 59, Hrmp: parachains_hrmp::{Pallet, Call, Storage, Event, Config} = 60, ParaSessionInfo: parachains_session_info::{Pallet, Storage} = 61, ParasDisputes: parachains_disputes::{Pallet, Call, Storage, Event} = 62, ParasSlashing: parachains_slashing::{Pallet, Call, Storage, ValidateUnsigned} = 63, + MessageQueue: pallet_message_queue::{Pallet, Call, Storage, Event} = 64, // Parachain Onboarding Pallets. Start indices at 70 to leave room. Registrar: paras_registrar::{Pallet, Call, Storage, Event, Config} = 70, @@ -1495,7 +1535,29 @@ pub mod migrations { ); /// Unreleased migrations. Add new ones here: - pub type Unreleased = (); + pub type Unreleased = ( + // Remove UMP dispatch queue + parachains_configuration::migration::v6::MigrateToV6, + ump_migrations::UpdateUmpLimits, + ); +} + +/// Helpers to configure all migrations. +pub mod ump_migrations { + use runtime_parachains::configuration::migration_ump; + + pub const MAX_UPWARD_QUEUE_SIZE: u32 = 8 * 1024 * 1024; + pub const MAX_UPWARD_QUEUE_COUNT: u32 = 1398101; + pub const MAX_UPWARD_MESSAGE_SIZE: u32 = (1 << 15) - 5; // Checked in test `max_upward_message_size`. + pub const MAX_UPWARD_MESSAGE_NUM_PER_CANDIDATE: u32 = 1024; + + pub type UpdateUmpLimits = migration_ump::latest::ScheduleConfigUpdate< + super::Runtime, + MAX_UPWARD_QUEUE_SIZE, + MAX_UPWARD_QUEUE_COUNT, + MAX_UPWARD_MESSAGE_SIZE, + MAX_UPWARD_MESSAGE_NUM_PER_CANDIDATE, + >; } /// Executive: handles dispatch to the various modules. @@ -1535,13 +1597,9 @@ frame_support::ord_parameter_types! { pub const MigController: AccountId = AccountId::from(hex_literal::hex!("52bc71c1eca5353749542dfdf0af97bf764f9c2f44e860cd485f1cd86400f649")); } -#[cfg(feature = "runtime-benchmarks")] -#[macro_use] -extern crate frame_benchmarking; - #[cfg(feature = "runtime-benchmarks")] mod benches { - define_benchmarks!( + frame_benchmarking::define_benchmarks!( // Polkadot // NOTE: Make sure to prefix these with `runtime_common::` so // the that path resolves correctly in the generated file. @@ -1553,10 +1611,10 @@ mod benches { [runtime_parachains::configuration, Configuration] [runtime_parachains::hrmp, Hrmp] [runtime_parachains::disputes, ParasDisputes] + [runtime_parachains::inclusion, ParaInclusion] [runtime_parachains::initializer, Initializer] [runtime_parachains::paras_inherent, ParaInherent] [runtime_parachains::paras, Paras] - [runtime_parachains::ump, Ump] // Substrate [pallet_balances, Balances] [pallet_balances, NisCounterpartBalances] @@ -1572,6 +1630,7 @@ mod benches { [pallet_im_online, ImOnline] [pallet_indices, Indices] [pallet_membership, TechnicalMembership] + [pallet_message_queue, MessageQueue] [pallet_multisig, Multisig] [pallet_preimage, Preimage] [pallet_proxy, Proxy] @@ -2164,6 +2223,20 @@ mod encoding_tests { } } +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn max_upward_message_size() { + use sp_core::Get; + assert_eq!( + ump_migrations::MAX_UPWARD_MESSAGE_SIZE, + pallet_message_queue::MaxMessageLenOf::::get() + ); + } +} + #[cfg(all(test, feature = "try-runtime"))] mod remote_tests { use super::*; diff --git a/runtime/rococo/src/weights/mod.rs b/runtime/rococo/src/weights/mod.rs index 908753f8540e..5bc39330e28e 100644 --- a/runtime/rococo/src/weights/mod.rs +++ b/runtime/rococo/src/weights/mod.rs @@ -28,6 +28,7 @@ pub mod pallet_identity; pub mod pallet_im_online; pub mod pallet_indices; pub mod pallet_membership; +pub mod pallet_message_queue; pub mod pallet_multisig; pub mod pallet_nis; pub mod pallet_preimage; @@ -49,8 +50,8 @@ pub mod runtime_common_slots; pub mod runtime_parachains_configuration; pub mod runtime_parachains_disputes; pub mod runtime_parachains_hrmp; +pub mod runtime_parachains_inclusion; pub mod runtime_parachains_initializer; pub mod runtime_parachains_paras; pub mod runtime_parachains_paras_inherent; -pub mod runtime_parachains_ump; pub mod xcm; diff --git a/runtime/rococo/src/weights/pallet_message_queue.rs b/runtime/rococo/src/weights/pallet_message_queue.rs new file mode 100644 index 000000000000..109125e22709 --- /dev/null +++ b/runtime/rococo/src/weights/pallet_message_queue.rs @@ -0,0 +1,176 @@ +// Copyright 2017-2022 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . +//! Autogenerated weights for `pallet_message_queue` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-02-28, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `i9`, CPU: `13th Gen Intel(R) Core(TM) i9-13900K` +//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: Some("rococo-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot +// benchmark +// pallet +// --chain=rococo-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet-message-queue +// --extrinsic=* +// --heap-pages=4096 +// --header=file_header.txt +// --output +// runtime/rococo/src/weights/pallet_message_queue.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for `pallet_message_queue`. +pub struct WeightInfo(PhantomData); +impl pallet_message_queue::WeightInfo for WeightInfo { + /// Storage: MessageQueue ServiceHead (r:1 w:0) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:2 w:2) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + fn ready_ring_knit() -> Weight { + // Proof Size summary in bytes: + // Measured: `837` + // Estimated: `5554` + // Minimum execution time: 9_427 nanoseconds. + Weight::from_parts(9_806_000, 0) + .saturating_add(Weight::from_parts(0, 5554)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: MessageQueue BookStateFor (r:2 w:2) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue ServiceHead (r:1 w:1) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + fn ready_ring_unknit() -> Weight { + // Proof Size summary in bytes: + // Measured: `837` + // Estimated: `5554` + // Minimum execution time: 20_178 nanoseconds. + Weight::from_parts(20_550_000, 0) + .saturating_add(Weight::from_parts(0, 5554)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + fn service_queue_base() -> Weight { + // Proof Size summary in bytes: + // Measured: `576` + // Estimated: `2527` + // Minimum execution time: 3_746 nanoseconds. + Weight::from_parts(3_885_000, 0) + .saturating_add(Weight::from_parts(0, 2527)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: MessageQueue Pages (r:1 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn service_page_base_completion() -> Weight { + // Proof Size summary in bytes: + // Measured: `648` + // Estimated: `68060` + // Minimum execution time: 4_906 nanoseconds. + Weight::from_parts(5_060_000, 0) + .saturating_add(Weight::from_parts(0, 68060)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: MessageQueue Pages (r:1 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn service_page_base_no_completion() -> Weight { + // Proof Size summary in bytes: + // Measured: `648` + // Estimated: `68060` + // Minimum execution time: 5_194 nanoseconds. + Weight::from_parts(5_361_000, 0) + .saturating_add(Weight::from_parts(0, 68060)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + fn service_page_item() -> Weight { + // Proof Size summary in bytes: + // Measured: `936` + // Estimated: `0` + // Minimum execution time: 62_021 nanoseconds. + Weight::from_parts(62_487_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: MessageQueue ServiceHead (r:1 w:1) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:1 w:0) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + fn bump_service_head() -> Weight { + // Proof Size summary in bytes: + // Measured: `712` + // Estimated: `3027` + // Minimum execution time: 6_989 nanoseconds. + Weight::from_parts(8_098_000, 0) + .saturating_add(Weight::from_parts(0, 3027)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:1 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn reap_page() -> Weight { + // Proof Size summary in bytes: + // Measured: `66846` + // Estimated: `70587` + // Minimum execution time: 38_177 nanoseconds. + Weight::from_parts(44_704_000, 0) + .saturating_add(Weight::from_parts(0, 70587)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:1 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn execute_overweight_page_removed() -> Weight { + // Proof Size summary in bytes: + // Measured: `66846` + // Estimated: `70587` + // Minimum execution time: 48_404 nanoseconds. + Weight::from_parts(55_066_000, 0) + .saturating_add(Weight::from_parts(0, 70587)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:1 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn execute_overweight_page_updated() -> Weight { + // Proof Size summary in bytes: + // Measured: `66846` + // Estimated: `70587` + // Minimum execution time: 66_523 nanoseconds. + Weight::from_parts(73_063_000, 0) + .saturating_add(Weight::from_parts(0, 70587)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } +} diff --git a/runtime/rococo/src/weights/runtime_parachains_inclusion.rs b/runtime/rococo/src/weights/runtime_parachains_inclusion.rs new file mode 100644 index 000000000000..83c1056951ec --- /dev/null +++ b/runtime/rococo/src/weights/runtime_parachains_inclusion.rs @@ -0,0 +1,66 @@ +// Copyright 2017-2022 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . +//! Autogenerated weights for `runtime_parachains::inclusion` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-02-23, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `i9`, CPU: `13th Gen Intel(R) Core(TM) i9-13900K` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("rococo-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot +// benchmark +// pallet +// --chain=rococo-dev +// --steps=50 +// --repeat=20 +// --pallet=runtime_parachains::inclusion +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --header=./file_header.txt +// --output=./runtime/rococo/src/weights + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for `runtime_parachains::inclusion`. +pub struct WeightInfo(PhantomData); +impl runtime_parachains::inclusion::WeightInfo for WeightInfo { + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:1 w:999) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + /// The range of component `i` is `[1, 1000]`. + fn receive_upward_messages(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `51490` + // Estimated: `70587` + // Minimum execution time: 48_782 nanoseconds. + Weight::from_parts(49_384_000, 0) + .saturating_add(Weight::from_parts(0, 70587)) + // Standard Error: 32_635 + .saturating_add(Weight::from_parts(43_384_796, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) + } +} diff --git a/runtime/rococo/src/weights/runtime_parachains_ump.rs b/runtime/rococo/src/weights/runtime_parachains_ump.rs deleted file mode 100644 index 35a393169571..000000000000 --- a/runtime/rococo/src/weights/runtime_parachains_ump.rs +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -//! Autogenerated weights for `runtime_parachains::ump` -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-04-28, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm4`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("rococo-dev"), DB CACHE: 1024 - -// Executed Command: -// ./target/production/polkadot -// benchmark -// pallet -// --chain=rococo-dev -// --steps=50 -// --repeat=20 -// --pallet=runtime_parachains::ump -// --extrinsic=* -// --execution=wasm -// --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/runtime_parachains_ump.rs - -#![cfg_attr(rustfmt, rustfmt_skip)] -#![allow(unused_parens)] -#![allow(unused_imports)] -#![allow(missing_docs)] - -use frame_support::{traits::Get, weights::Weight}; -use core::marker::PhantomData; - -/// Weight functions for `runtime_parachains::ump`. -pub struct WeightInfo(PhantomData); -impl runtime_parachains::ump::WeightInfo for WeightInfo { - /// The range of component `s` is `[0, 51200]`. - fn process_upward_message(s: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 6_036_000 picoseconds. - Weight::from_parts(6_133_000, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 8 - .saturating_add(Weight::from_parts(1_360, 0).saturating_mul(s.into())) - } - /// Storage: Ump NeedsDispatch (r:1 w:1) - /// Proof Skipped: Ump NeedsDispatch (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Ump NextDispatchRoundStartWith (r:1 w:1) - /// Proof Skipped: Ump NextDispatchRoundStartWith (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Ump RelayDispatchQueues (r:0 w:1) - /// Proof Skipped: Ump RelayDispatchQueues (max_values: None, max_size: None, mode: Measured) - /// Storage: Ump RelayDispatchQueueSize (r:0 w:1) - /// Proof Skipped: Ump RelayDispatchQueueSize (max_values: None, max_size: None, mode: Measured) - fn clean_ump_after_outgoing() -> Weight { - // Proof Size summary in bytes: - // Measured: `202` - // Estimated: `1687` - // Minimum execution time: 8_630_000 picoseconds. - Weight::from_parts(8_883_000, 0) - .saturating_add(Weight::from_parts(0, 1687)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(4)) - } - /// Storage: Ump Overweight (r:1 w:1) - /// Proof Skipped: Ump Overweight (max_values: None, max_size: None, mode: Measured) - /// Storage: Ump CounterForOverweight (r:1 w:1) - /// Proof: Ump CounterForOverweight (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - fn service_overweight() -> Weight { - // Proof Size summary in bytes: - // Measured: `219` - // Estimated: `3684` - // Minimum execution time: 22_583_000 picoseconds. - Weight::from_parts(22_844_000, 0) - .saturating_add(Weight::from_parts(0, 3684)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(2)) - } -} diff --git a/runtime/test-runtime/src/lib.rs b/runtime/test-runtime/src/lib.rs index 41387fa6bb94..cdae8b276877 100644 --- a/runtime/test-runtime/src/lib.rs +++ b/runtime/test-runtime/src/lib.rs @@ -30,7 +30,7 @@ use polkadot_runtime_parachains::{ initializer as parachains_initializer, origin as parachains_origin, paras as parachains_paras, paras_inherent as parachains_paras_inherent, runtime_api_impl::v4 as runtime_impl, scheduler as parachains_scheduler, session_info as parachains_session_info, - shared as parachains_shared, ump as parachains_ump, + shared as parachains_shared, }; use authority_discovery_primitives::AuthorityId as AuthorityDiscoveryId; @@ -477,6 +477,8 @@ impl parachains_inclusion::Config for Runtime { type RuntimeEvent = RuntimeEvent; type DisputesHandler = ParasDisputes; type RewardValidators = RewardValidatorsWithEraPoints; + type MessageQueue = (); + type WeightInfo = (); } impl parachains_disputes::Config for Runtime { @@ -508,6 +510,7 @@ impl parachains_paras::Config for Runtime { type RuntimeEvent = RuntimeEvent; type WeightInfo = parachains_paras::TestWeightInfo; type UnsignedPriority = ParasUnsignedPriority; + type QueueFootprinter = ParaInclusion; type NextSessionRotation = Babe; } @@ -517,14 +520,6 @@ parameter_types! { pub const FirstMessageFactorPercent: u64 = 100; } -impl parachains_ump::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type UmpSink = (); - type FirstMessageFactorPercent = FirstMessageFactorPercent; - type ExecuteOverweightOrigin = frame_system::EnsureRoot; - type WeightInfo = parachains_ump::TestWeightInfo; -} - impl parachains_hrmp::Config for Runtime { type RuntimeOrigin = RuntimeOrigin; type RuntimeEvent = RuntimeEvent; @@ -672,7 +667,6 @@ construct_runtime! { ParasOrigin: parachains_origin::{Pallet, Origin}, ParaSessionInfo: parachains_session_info::{Pallet, Storage}, Hrmp: parachains_hrmp::{Pallet, Call, Storage, Event}, - Ump: parachains_ump::{Pallet, Call, Storage, Event}, Dmp: parachains_dmp::{Pallet, Storage}, Xcm: pallet_xcm::{Pallet, Call, Event, Origin}, ParasDisputes: parachains_disputes::{Pallet, Storage, Event}, diff --git a/runtime/westend/Cargo.toml b/runtime/westend/Cargo.toml index 77df86877fe6..08a8ca23a55a 100644 --- a/runtime/westend/Cargo.toml +++ b/runtime/westend/Cargo.toml @@ -54,6 +54,7 @@ pallet-identity = { git = "https://github.com/paritytech/substrate", branch = "m pallet-im-online = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-indices = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-membership = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-message-queue = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-multisig = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-nomination-pools = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-offences = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -144,6 +145,7 @@ std = [ "pallet-im-online/std", "pallet-indices/std", "pallet-membership/std", + "pallet-message-queue/std", "beefy-primitives/std", "pallet-multisig/std", "pallet-nomination-pools/std", @@ -206,6 +208,8 @@ runtime-benchmarks = [ "pallet-identity/runtime-benchmarks", "pallet-im-online/runtime-benchmarks", "pallet-indices/runtime-benchmarks", + "pallet-membership/runtime-benchmarks", + "pallet-message-queue/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", "pallet-nomination-pools-benchmarking/runtime-benchmarks", "pallet-preimage/runtime-benchmarks", @@ -249,6 +253,7 @@ try-runtime = [ "pallet-im-online/try-runtime", "pallet-indices/try-runtime", "pallet-membership/try-runtime", + "pallet-message-queue/try-runtime", "pallet-multisig/try-runtime", "pallet-nomination-pools/try-runtime", "pallet-offences/try-runtime", diff --git a/runtime/westend/src/lib.rs b/runtime/westend/src/lib.rs index 538900f83a78..5ca12202fadf 100644 --- a/runtime/westend/src/lib.rs +++ b/runtime/westend/src/lib.rs @@ -25,8 +25,11 @@ use beefy_primitives::crypto::{AuthorityId as BeefyId, Signature as BeefySignatu use frame_election_provider_support::{onchain, SequentialPhragmen}; use frame_support::{ construct_runtime, parameter_types, - traits::{ConstU32, InstanceFilter, KeyOwnerProofSystem, WithdrawReasons}, - weights::ConstantMultiplier, + traits::{ + ConstU32, InstanceFilter, KeyOwnerProofSystem, ProcessMessage, ProcessMessageError, + WithdrawReasons, + }, + weights::{ConstantMultiplier, WeightMeter}, PalletId, }; use frame_system::EnsureRoot; @@ -50,12 +53,14 @@ use runtime_common::{ }; use runtime_parachains::{ configuration as parachains_configuration, disputes as parachains_disputes, - disputes::slashing as parachains_slashing, dmp as parachains_dmp, hrmp as parachains_hrmp, - inclusion as parachains_inclusion, initializer as parachains_initializer, - origin as parachains_origin, paras as parachains_paras, + disputes::slashing as parachains_slashing, + dmp as parachains_dmp, hrmp as parachains_hrmp, inclusion as parachains_inclusion, + inclusion::{AggregateMessageOrigin, UmpQueueId}, + initializer as parachains_initializer, origin as parachains_origin, paras as parachains_paras, paras_inherent as parachains_paras_inherent, reward_points as parachains_reward_points, - runtime_api_impl::v4 as parachains_runtime_api_impl, scheduler as parachains_scheduler, - session_info as parachains_session_info, shared as parachains_shared, ump as parachains_ump, + runtime_api_impl::v4 as parachains_runtime_api_impl, + scheduler as parachains_scheduler, session_info as parachains_session_info, + shared as parachains_shared, }; use scale_info::TypeInfo; use sp_core::{OpaqueMetadata, RuntimeDebug}; @@ -76,6 +81,7 @@ use sp_std::{collections::btree_map::BTreeMap, prelude::*}; #[cfg(any(feature = "std", test))] use sp_version::NativeVersion; use sp_version::RuntimeVersion; +use xcm::latest::Junction; pub use frame_system::Call as SystemCall; pub use pallet_balances::Call as BalancesCall; @@ -895,6 +901,8 @@ impl parachains_inclusion::Config for Runtime { type RuntimeEvent = RuntimeEvent; type DisputesHandler = ParasDisputes; type RewardValidators = parachains_reward_points::RewardValidatorsWithEraPoints; + type MessageQueue = MessageQueue; + type WeightInfo = weights::runtime_parachains_inclusion::WeightInfo; } parameter_types! { @@ -905,20 +913,55 @@ impl parachains_paras::Config for Runtime { type RuntimeEvent = RuntimeEvent; type WeightInfo = weights::runtime_parachains_paras::WeightInfo; type UnsignedPriority = ParasUnsignedPriority; + type QueueFootprinter = ParaInclusion; type NextSessionRotation = Babe; } parameter_types! { - pub const FirstMessageFactorPercent: u64 = 100; + /// Amount of weight that can be spent per block to service messages. + /// + /// # WARNING + /// + /// This is not a good value for para-chains since the `Scheduler` already uses up to 80% block weight. + pub MessageQueueServiceWeight: Weight = Perbill::from_percent(20) * BlockWeights::get().max_block; + pub const MessageQueueHeapSize: u32 = 128 * 1024; + pub const MessageQueueMaxStale: u32 = 48; +} + +/// Message processor to handle any messages that were enqueued into the `MessageQueue` pallet. +pub struct MessageProcessor; +impl ProcessMessage for MessageProcessor { + type Origin = AggregateMessageOrigin; + + fn process_message( + message: &[u8], + origin: Self::Origin, + meter: &mut WeightMeter, + ) -> Result { + let para = match origin { + AggregateMessageOrigin::Ump(UmpQueueId::Para(para)) => para, + }; + xcm_builder::ProcessXcmMessage::< + Junction, + xcm_executor::XcmExecutor, + RuntimeCall, + >::process_message(message, Junction::Parachain(para.into()), meter) + } } -impl parachains_ump::Config for Runtime { +impl pallet_message_queue::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type UmpSink = - crate::parachains_ump::XcmSink, Runtime>; - type FirstMessageFactorPercent = FirstMessageFactorPercent; - type ExecuteOverweightOrigin = EnsureRoot; - type WeightInfo = weights::runtime_parachains_ump::WeightInfo; + type Size = u32; + type HeapSize = MessageQueueHeapSize; + type MaxStale = MessageQueueMaxStale; + type ServiceWeight = MessageQueueServiceWeight; + #[cfg(not(feature = "runtime-benchmarks"))] + type MessageProcessor = MessageProcessor; + #[cfg(feature = "runtime-benchmarks")] + type MessageProcessor = + pallet_message_queue::mock_helpers::NoopMessageProcessor; + type QueueChangeHandler = ParaInclusion; + type WeightInfo = weights::pallet_message_queue::WeightInfo; } impl parachains_dmp::Config for Runtime {} @@ -1162,7 +1205,7 @@ construct_runtime! { Paras: parachains_paras::{Pallet, Call, Storage, Event, Config, ValidateUnsigned} = 47, Initializer: parachains_initializer::{Pallet, Call, Storage} = 48, Dmp: parachains_dmp::{Pallet, Storage} = 49, - Ump: parachains_ump::{Pallet, Call, Storage, Event} = 50, + // RIP Ump 50 Hrmp: parachains_hrmp::{Pallet, Call, Storage, Event, Config} = 51, ParaSessionInfo: parachains_session_info::{Pallet, Storage} = 52, ParasDisputes: parachains_disputes::{Pallet, Call, Storage, Event} = 53, @@ -1178,6 +1221,9 @@ construct_runtime! { // Pallet for sending XCM. XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event, Origin, Config} = 99, + + // Generalized message queue + MessageQueue: pallet_message_queue::{Pallet, Call, Storage, Event} = 100, } } @@ -1237,7 +1283,29 @@ pub mod migrations { ); /// Unreleased migrations. Add new ones here: - pub type Unreleased = (); + pub type Unreleased = ( + // Remove UMP dispatch queue + parachains_configuration::migration::v6::MigrateToV6, + ump_migrations::UpdateUmpLimits, + ); +} + +/// Helpers to configure all migrations. +pub mod ump_migrations { + use runtime_parachains::configuration::migration_ump; + + pub const MAX_UPWARD_QUEUE_SIZE: u32 = 8 * 1024 * 1024; + pub const MAX_UPWARD_QUEUE_COUNT: u32 = 1398101; + pub const MAX_UPWARD_MESSAGE_SIZE: u32 = (1 << 17) - 5; // Checked in test `max_upward_message_size`. + pub const MAX_UPWARD_MESSAGE_NUM_PER_CANDIDATE: u32 = 512; + + pub type UpdateUmpLimits = migration_ump::latest::ScheduleConfigUpdate< + super::Runtime, + MAX_UPWARD_QUEUE_SIZE, + MAX_UPWARD_QUEUE_COUNT, + MAX_UPWARD_MESSAGE_SIZE, + MAX_UPWARD_MESSAGE_NUM_PER_CANDIDATE, + >; } /// Unchecked extrinsic type as expected by this runtime. @@ -1255,13 +1323,9 @@ pub type Executive = frame_executive::Executive< /// The payload being signed in transactions. pub type SignedPayload = generic::SignedPayload; -#[cfg(feature = "runtime-benchmarks")] -#[macro_use] -extern crate frame_benchmarking; - #[cfg(feature = "runtime-benchmarks")] mod benches { - define_benchmarks!( + frame_benchmarking::define_benchmarks!( // Polkadot // NOTE: Make sure to prefix these with `runtime_common::` so // the that path resolves correctly in the generated file. @@ -1273,10 +1337,10 @@ mod benches { [runtime_parachains::disputes, ParasDisputes] [runtime_parachains::disputes::slashing, ParasSlashing] [runtime_parachains::hrmp, Hrmp] + [runtime_parachains::inclusion, ParaInclusion] [runtime_parachains::initializer, Initializer] [runtime_parachains::paras, Paras] [runtime_parachains::paras_inherent, ParaInherent] - [runtime_parachains::ump, Ump] // Substrate [pallet_bags_list, VoterList] [pallet_balances, Balances] @@ -1286,6 +1350,7 @@ mod benches { [pallet_identity, Identity] [pallet_im_online, ImOnline] [pallet_indices, Indices] + [pallet_message_queue, MessageQueue] [pallet_multisig, Multisig] [pallet_nomination_pools, NominationPoolsBench::] [pallet_offences, OffencesBench::] @@ -1902,6 +1967,19 @@ sp_api::impl_runtime_apis! { } } +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn max_upward_message_size() { + assert_eq!( + ump_migrations::MAX_UPWARD_MESSAGE_SIZE, + pallet_message_queue::MaxMessageLenOf::::get() + ); + } +} + #[cfg(all(test, feature = "try-runtime"))] mod remote_tests { use super::*; diff --git a/runtime/westend/src/tests.rs b/runtime/westend/src/tests.rs index 226410b0729e..8f835ea49a86 100644 --- a/runtime/westend/src/tests.rs +++ b/runtime/westend/src/tests.rs @@ -50,6 +50,14 @@ fn call_size() { ); } +#[test] +fn max_upward_message_size() { + assert_eq!( + ump_migrations::MAX_UPWARD_MESSAGE_SIZE, + pallet_message_queue::MaxMessageLenOf::::get() + ); +} + #[test] fn sanity_check_teleport_assets_weight() { // This test sanity checks that at least 50 teleports can exist in a block. diff --git a/runtime/westend/src/weights/mod.rs b/runtime/westend/src/weights/mod.rs index ba787031ec2e..6341b3da8b69 100644 --- a/runtime/westend/src/weights/mod.rs +++ b/runtime/westend/src/weights/mod.rs @@ -24,6 +24,7 @@ pub mod pallet_fast_unstake; pub mod pallet_identity; pub mod pallet_im_online; pub mod pallet_indices; +pub mod pallet_message_queue; pub mod pallet_multisig; pub mod pallet_nomination_pools; pub mod pallet_preimage; @@ -44,8 +45,8 @@ pub mod runtime_parachains_configuration; pub mod runtime_parachains_disputes; pub mod runtime_parachains_disputes_slashing; pub mod runtime_parachains_hrmp; +pub mod runtime_parachains_inclusion; pub mod runtime_parachains_initializer; pub mod runtime_parachains_paras; pub mod runtime_parachains_paras_inherent; -pub mod runtime_parachains_ump; pub mod xcm; diff --git a/runtime/westend/src/weights/pallet_message_queue.rs b/runtime/westend/src/weights/pallet_message_queue.rs new file mode 100644 index 000000000000..665c0505cb04 --- /dev/null +++ b/runtime/westend/src/weights/pallet_message_queue.rs @@ -0,0 +1,176 @@ +// Copyright 2017-2022 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . +//! Autogenerated weights for `pallet_message_queue` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-02-28, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `i9`, CPU: `13th Gen Intel(R) Core(TM) i9-13900K` +//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: Some("westend-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot +// benchmark +// pallet +// --chain=westend-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet-message-queue +// --extrinsic=* +// --heap-pages=4096 +// --header=file_header.txt +// --output +// runtime/westend/src/weights/pallet_message_queue.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for `pallet_message_queue`. +pub struct WeightInfo(PhantomData); +impl pallet_message_queue::WeightInfo for WeightInfo { + /// Storage: MessageQueue ServiceHead (r:1 w:0) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:2 w:2) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + fn ready_ring_knit() -> Weight { + // Proof Size summary in bytes: + // Measured: `804` + // Estimated: `5554` + // Minimum execution time: 5_813 nanoseconds. + Weight::from_parts(5_980_000, 0) + .saturating_add(Weight::from_parts(0, 5554)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: MessageQueue BookStateFor (r:2 w:2) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue ServiceHead (r:1 w:1) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + fn ready_ring_unknit() -> Weight { + // Proof Size summary in bytes: + // Measured: `804` + // Estimated: `5554` + // Minimum execution time: 5_742 nanoseconds. + Weight::from_parts(5_986_000, 0) + .saturating_add(Weight::from_parts(0, 5554)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + fn service_queue_base() -> Weight { + // Proof Size summary in bytes: + // Measured: `543` + // Estimated: `2527` + // Minimum execution time: 2_118 nanoseconds. + Weight::from_parts(2_206_000, 0) + .saturating_add(Weight::from_parts(0, 2527)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: MessageQueue Pages (r:1 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn service_page_base_completion() -> Weight { + // Proof Size summary in bytes: + // Measured: `615` + // Estimated: `68060` + // Minimum execution time: 3_153 nanoseconds. + Weight::from_parts(3_240_000, 0) + .saturating_add(Weight::from_parts(0, 68060)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: MessageQueue Pages (r:1 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn service_page_base_no_completion() -> Weight { + // Proof Size summary in bytes: + // Measured: `615` + // Estimated: `68060` + // Minimum execution time: 3_191 nanoseconds. + Weight::from_parts(3_289_000, 0) + .saturating_add(Weight::from_parts(0, 68060)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + fn service_page_item() -> Weight { + // Proof Size summary in bytes: + // Measured: `904` + // Estimated: `0` + // Minimum execution time: 47_975 nanoseconds. + Weight::from_parts(49_334_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: MessageQueue ServiceHead (r:1 w:1) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:1 w:0) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + fn bump_service_head() -> Weight { + // Proof Size summary in bytes: + // Measured: `679` + // Estimated: `3027` + // Minimum execution time: 3_725 nanoseconds. + Weight::from_parts(4_074_000, 0) + .saturating_add(Weight::from_parts(0, 3027)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:1 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn reap_page() -> Weight { + // Proof Size summary in bytes: + // Measured: `66814` + // Estimated: `70587` + // Minimum execution time: 28_769 nanoseconds. + Weight::from_parts(29_450_000, 0) + .saturating_add(Weight::from_parts(0, 70587)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:1 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn execute_overweight_page_removed() -> Weight { + // Proof Size summary in bytes: + // Measured: `66814` + // Estimated: `70587` + // Minimum execution time: 74_422 nanoseconds. + Weight::from_parts(78_637_000, 0) + .saturating_add(Weight::from_parts(0, 70587)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:1 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn execute_overweight_page_updated() -> Weight { + // Proof Size summary in bytes: + // Measured: `66814` + // Estimated: `70587` + // Minimum execution time: 75_542 nanoseconds. + Weight::from_parts(80_545_000, 0) + .saturating_add(Weight::from_parts(0, 70587)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } +} diff --git a/runtime/westend/src/weights/runtime_parachains_inclusion.rs b/runtime/westend/src/weights/runtime_parachains_inclusion.rs new file mode 100644 index 000000000000..112abc3aa181 --- /dev/null +++ b/runtime/westend/src/weights/runtime_parachains_inclusion.rs @@ -0,0 +1,66 @@ +// Copyright 2017-2022 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . +//! Autogenerated weights for `runtime_parachains::inclusion` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-02-23, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `i9`, CPU: `13th Gen Intel(R) Core(TM) i9-13900K` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("westend-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot +// benchmark +// pallet +// --chain=westend-dev +// --steps=50 +// --repeat=20 +// --pallet=runtime_parachains::inclusion +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --header=./file_header.txt +// --output=./runtime/westend/src/weights + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for `runtime_parachains::inclusion`. +pub struct WeightInfo(PhantomData); +impl runtime_parachains::inclusion::WeightInfo for WeightInfo { + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:1 w:999) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + /// The range of component `i` is `[1, 1000]`. + fn receive_upward_messages(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `51490` + // Estimated: `70587` + // Minimum execution time: 49_053 nanoseconds. + Weight::from_parts(49_506_000, 0) + .saturating_add(Weight::from_parts(0, 70587)) + // Standard Error: 62_734 + .saturating_add(Weight::from_parts(43_511_974, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) + } +} diff --git a/runtime/westend/src/weights/runtime_parachains_ump.rs b/runtime/westend/src/weights/runtime_parachains_ump.rs deleted file mode 100644 index 13b291e867d3..000000000000 --- a/runtime/westend/src/weights/runtime_parachains_ump.rs +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -//! Autogenerated weights for `runtime_parachains::ump` -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-04-28, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm6`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("westend-dev"), DB CACHE: 1024 - -// Executed Command: -// ./target/production/polkadot -// benchmark -// pallet -// --chain=westend-dev -// --steps=50 -// --repeat=20 -// --pallet=runtime_parachains::ump -// --extrinsic=* -// --execution=wasm -// --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/westend/src/weights/runtime_parachains_ump.rs - -#![cfg_attr(rustfmt, rustfmt_skip)] -#![allow(unused_parens)] -#![allow(unused_imports)] -#![allow(missing_docs)] - -use frame_support::{traits::Get, weights::Weight}; -use core::marker::PhantomData; - -/// Weight functions for `runtime_parachains::ump`. -pub struct WeightInfo(PhantomData); -impl runtime_parachains::ump::WeightInfo for WeightInfo { - /// The range of component `s` is `[0, 51200]`. - fn process_upward_message(s: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 6_172_000 picoseconds. - Weight::from_parts(6_259_000, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 8 - .saturating_add(Weight::from_parts(1_359, 0).saturating_mul(s.into())) - } - /// Storage: Ump NeedsDispatch (r:1 w:1) - /// Proof Skipped: Ump NeedsDispatch (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Ump NextDispatchRoundStartWith (r:1 w:1) - /// Proof Skipped: Ump NextDispatchRoundStartWith (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Ump RelayDispatchQueues (r:0 w:1) - /// Proof Skipped: Ump RelayDispatchQueues (max_values: None, max_size: None, mode: Measured) - /// Storage: Ump RelayDispatchQueueSize (r:0 w:1) - /// Proof Skipped: Ump RelayDispatchQueueSize (max_values: None, max_size: None, mode: Measured) - fn clean_ump_after_outgoing() -> Weight { - // Proof Size summary in bytes: - // Measured: `206` - // Estimated: `1691` - // Minimum execution time: 9_533_000 picoseconds. - Weight::from_parts(9_765_000, 0) - .saturating_add(Weight::from_parts(0, 1691)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(4)) - } - /// Storage: Ump Overweight (r:1 w:1) - /// Proof Skipped: Ump Overweight (max_values: None, max_size: None, mode: Measured) - /// Storage: Ump CounterForOverweight (r:1 w:1) - /// Proof: Ump CounterForOverweight (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - fn service_overweight() -> Weight { - // Proof Size summary in bytes: - // Measured: `223` - // Estimated: `3688` - // Minimum execution time: 23_648_000 picoseconds. - Weight::from_parts(23_848_000, 0) - .saturating_add(Weight::from_parts(0, 3688)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(2)) - } -} diff --git a/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs b/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs index 382434ea1c28..15fc97544a9e 100644 --- a/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs +++ b/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs @@ -27,7 +27,6 @@ use sp_core::H256; use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, }; use xcm::latest::prelude::*; use xcm_builder::{AllowUnpaidExecutionFrom, MintLocation}; @@ -206,7 +205,9 @@ impl xcm_balances_benchmark::Config for Test { } } +#[cfg(feature = "runtime-benchmarks")] pub fn new_test_ext() -> sp_io::TestExternalities { + use sp_runtime::BuildStorage; let t = GenesisConfig { ..Default::default() }.build_storage().unwrap(); sp_tracing::try_init_simple(); t.into() diff --git a/xcm/pallet-xcm-benchmarks/src/generic/mock.rs b/xcm/pallet-xcm-benchmarks/src/generic/mock.rs index df6e7713b8a9..9110d55f800a 100644 --- a/xcm/pallet-xcm-benchmarks/src/generic/mock.rs +++ b/xcm/pallet-xcm-benchmarks/src/generic/mock.rs @@ -27,7 +27,6 @@ use sp_core::H256; use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentityLookup, TrailingZeroInput}, - BuildStorage, }; use xcm_builder::{ test_utils::{ @@ -194,7 +193,9 @@ impl generic::Config for Test { } } +#[cfg(feature = "runtime-benchmarks")] pub fn new_test_ext() -> sp_io::TestExternalities { + use sp_runtime::BuildStorage; let t = GenesisConfig { ..Default::default() }.build_storage().unwrap(); sp_tracing::try_init_simple(); t.into() diff --git a/xcm/xcm-builder/Cargo.toml b/xcm/xcm-builder/Cargo.toml index 62bc0a0bf878..6f4a4f9dde14 100644 --- a/xcm/xcm-builder/Cargo.toml +++ b/xcm/xcm-builder/Cargo.toml @@ -15,6 +15,7 @@ sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", d sp-arithmetic = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-weights = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -29,6 +30,7 @@ pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "m pallet-xcm = { path = "../pallet-xcm" } polkadot-runtime-parachains = { path = "../../runtime/parachains" } assert_matches = "1.5.0" +polkadot-test-runtime = { path = "../../runtime/test-runtime" } [features] default = ["std"] @@ -46,6 +48,7 @@ std = [ "sp-arithmetic/std", "sp-io/std", "sp-runtime/std", + "sp-weights/std", "frame-support/std", "frame-system/std", "polkadot-parachain/std", diff --git a/xcm/xcm-builder/src/lib.rs b/xcm/xcm-builder/src/lib.rs index e6f4db1befd2..0f1753474624 100644 --- a/xcm/xcm-builder/src/lib.rs +++ b/xcm/xcm-builder/src/lib.rs @@ -54,6 +54,9 @@ pub use barriers::{ RespectSuspension, TakeWeightCredit, WithComputedOrigin, }; +mod process_xcm_message; +pub use process_xcm_message::ProcessXcmMessage; + mod currency_adapter; pub use currency_adapter::CurrencyAdapter; diff --git a/xcm/xcm-builder/src/process_xcm_message.rs b/xcm/xcm-builder/src/process_xcm_message.rs new file mode 100644 index 000000000000..23e2ed2974fc --- /dev/null +++ b/xcm/xcm-builder/src/process_xcm_message.rs @@ -0,0 +1,154 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Implementation of `ProcessMessage` for an `ExecuteXcm` implementation. + +use frame_support::{ + ensure, + traits::{ProcessMessage, ProcessMessageError}, +}; +use parity_scale_codec::{Decode, FullCodec, MaxEncodedLen}; +use scale_info::TypeInfo; +use sp_io::hashing::blake2_256; +use sp_std::{fmt::Debug, marker::PhantomData}; +use sp_weights::{Weight, WeightMeter}; +use xcm::prelude::*; + +/// A message processor that delegates execution to an [XcmExecutor]. +pub struct ProcessXcmMessage( + PhantomData<(MessageOrigin, XcmExecutor, Call)>, +); +impl< + MessageOrigin: Into + FullCodec + MaxEncodedLen + Clone + Eq + PartialEq + TypeInfo + Debug, + XcmExecutor: ExecuteXcm, + Call, + > ProcessMessage for ProcessXcmMessage +{ + type Origin = MessageOrigin; + + /// Process the given message, using no more than the remaining `weight` to do so. + fn process_message( + message: &[u8], + origin: Self::Origin, + meter: &mut WeightMeter, + ) -> Result { + let hash = blake2_256(message); + let versioned_message = VersionedXcm::::decode(&mut &message[..]) + .map_err(|_| ProcessMessageError::Corrupt)?; + let message = Xcm::::try_from(versioned_message) + .map_err(|_| ProcessMessageError::Unsupported)?; + let pre = XcmExecutor::prepare(message).map_err(|_| ProcessMessageError::Unsupported)?; + let required = pre.weight_of(); + ensure!(meter.can_accrue(required), ProcessMessageError::Overweight(required)); + + let (consumed, result) = + match XcmExecutor::execute(origin.into(), pre, hash, Weight::zero()) { + Outcome::Complete(w) => (w, Ok(true)), + Outcome::Incomplete(w, _) => (w, Ok(false)), + // In the error-case we assume the worst case and consume all possible weight. + Outcome::Error(_) => (required, Err(ProcessMessageError::Unsupported)), + }; + meter.defensive_saturating_accrue(consumed); + result + } +} + +#[cfg(test)] +mod tests { + use super::*; + use frame_support::{ + assert_err, assert_ok, + traits::{ProcessMessageError, ProcessMessageError::*}, + }; + use parity_scale_codec::Encode; + use polkadot_test_runtime::*; + use xcm::{v2, v3, VersionedXcm}; + + const ORIGIN: Junction = Junction::OnlyChild; + /// The processor to use for tests. + type Processor = + ProcessXcmMessage, RuntimeCall>; + + #[test] + fn process_message_trivial_works() { + // ClearOrigin works. + assert!(process(v2_xcm(true)).unwrap()); + assert!(process(v3_xcm(true)).unwrap()); + } + + #[test] + fn process_message_trivial_fails() { + // Trap makes it fail. + assert!(!process(v3_xcm(false)).unwrap()); + assert!(!process(v3_xcm(false)).unwrap()); + } + + #[test] + fn process_message_corrupted_fails() { + let msgs: &[&[u8]] = &[&[], &[55, 66], &[123, 222, 233]]; + for msg in msgs { + assert_err!(process_raw(msg), Corrupt); + } + } + + #[test] + fn process_message_overweight_fails() { + for msg in [v3_xcm(true), v3_xcm(false), v3_xcm(false), v2_xcm(false)] { + let msg = &msg.encode()[..]; + + // Errors if we stay below a weight limit of 1000. + for i in 0..10 { + let meter = &mut WeightMeter::from_limit((i * 10).into()); + assert_err!( + Processor::process_message(msg, ORIGIN, meter), + Overweight(1000.into()) + ); + assert_eq!(meter.consumed, 0.into()); + } + + // Works with a limit of 1000. + let meter = &mut WeightMeter::from_limit(1000.into()); + assert_ok!(Processor::process_message(msg, ORIGIN, meter)); + assert_eq!(meter.consumed, 1000.into()); + } + } + + fn v2_xcm(success: bool) -> VersionedXcm { + let instr = if success { + v3::Instruction::::ClearOrigin + } else { + v3::Instruction::::Trap(1) + }; + VersionedXcm::V3(v3::Xcm::(vec![instr])) + } + + fn v3_xcm(success: bool) -> VersionedXcm { + let instr = if success { + v2::Instruction::::ClearOrigin + } else { + v2::Instruction::::Trap(1) + }; + VersionedXcm::V2(v2::Xcm::(vec![instr])) + } + + fn process(msg: VersionedXcm) -> Result { + process_raw(msg.encode().as_slice()) + } + + fn process_raw(raw: &[u8]) -> Result { + Processor::process_message(raw, ORIGIN, &mut WeightMeter::max_limit()) + } +} diff --git a/xcm/xcm-simulator/Cargo.toml b/xcm/xcm-simulator/Cargo.toml index 62674ed638d5..5a4589659103 100644 --- a/xcm/xcm-simulator/Cargo.toml +++ b/xcm/xcm-simulator/Cargo.toml @@ -15,6 +15,7 @@ sp-std = { git = "https://github.com/paritytech/substrate", branch = "master" } xcm = { path = "../" } xcm-executor = { path = "../xcm-executor" } +xcm-builder = { path = "../xcm-builder" } polkadot-core-primitives = { path = "../../core-primitives"} polkadot-parachain = { path = "../../parachain" } polkadot-runtime-parachains = { path = "../../runtime/parachains" } diff --git a/xcm/xcm-simulator/example/Cargo.toml b/xcm/xcm-simulator/example/Cargo.toml index 64dc0b8058cd..47b7c82243d3 100644 --- a/xcm/xcm-simulator/example/Cargo.toml +++ b/xcm/xcm-simulator/example/Cargo.toml @@ -13,6 +13,7 @@ log = { version = "0.4.14", default-features = false } frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" } frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-message-queue = { git = "https://github.com/paritytech/substrate", branch = "master" } pallet-uniques = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-std = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } @@ -35,6 +36,7 @@ runtime-benchmarks = [ "frame-system/runtime-benchmarks", "frame-support/runtime-benchmarks", "pallet-balances/runtime-benchmarks", + "pallet-message-queue/runtime-benchmarks", "pallet-uniques/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", "xcm-builder/runtime-benchmarks", diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index bd439192215e..33a5b2c70a9f 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -20,7 +20,7 @@ mod relay_chain; use frame_support::sp_tracing; use xcm::prelude::*; use xcm_executor::traits::Convert; -use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain}; +use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain, TestExt}; pub const ALICE: sp_runtime::AccountId32 = sp_runtime::AccountId32::new([0u8; 32]); pub const INITIAL_BALANCE: u128 = 1_000_000_000; @@ -46,7 +46,11 @@ decl_test_parachain! { decl_test_relay_chain! { pub struct Relay { Runtime = relay_chain::Runtime, + RuntimeCall = relay_chain::RuntimeCall, + RuntimeEvent = relay_chain::RuntimeEvent, XcmConfig = relay_chain::XcmConfig, + MessageQueue = relay_chain::MessageQueue, + System = relay_chain::System, new_ext = relay_ext(), } } diff --git a/xcm/xcm-simulator/example/src/relay_chain.rs b/xcm/xcm-simulator/example/src/relay_chain.rs index a6585b74c106..9d66c9b7954b 100644 --- a/xcm/xcm-simulator/example/src/relay_chain.rs +++ b/xcm/xcm-simulator/example/src/relay_chain.rs @@ -18,8 +18,8 @@ use frame_support::{ construct_runtime, parameter_types, - traits::{AsEnsureOriginWithArg, Everything, Nothing}, - weights::Weight, + traits::{AsEnsureOriginWithArg, Everything, Nothing, ProcessMessage, ProcessMessageError}, + weights::{Weight, WeightMeter}, }; use frame_system::EnsureRoot; @@ -27,7 +27,11 @@ use sp_core::{ConstU32, H256}; use sp_runtime::{testing::Header, traits::IdentityLookup, AccountId32}; use polkadot_parachain::primitives::Id as ParaId; -use polkadot_runtime_parachains::{configuration, origin, shared, ump}; +use polkadot_runtime_parachains::{ + configuration, + inclusion::{AggregateMessageOrigin, UmpQueueId}, + origin, shared, +}; use xcm::latest::prelude::*; use xcm_builder::{ Account32Hash, AccountId32Aliases, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, @@ -232,19 +236,50 @@ parameter_types! { pub const FirstMessageFactorPercent: u64 = 100; } -impl ump::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type UmpSink = ump::XcmSink, Runtime>; - type FirstMessageFactorPercent = FirstMessageFactorPercent; - type ExecuteOverweightOrigin = frame_system::EnsureRoot; - type WeightInfo = ump::TestWeightInfo; -} - impl origin::Config for Runtime {} type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; +parameter_types! { + /// Amount of weight that can be spent per block to service messages. + pub MessageQueueServiceWeight: Weight = Weight::from_parts(1_000_000_000, 1_000_000); + pub const MessageQueueHeapSize: u32 = 65_536; + pub const MessageQueueMaxStale: u32 = 16; +} + +/// Message processor to handle any messages that were enqueued into the `MessageQueue` pallet. +pub struct MessageProcessor; +impl ProcessMessage for MessageProcessor { + type Origin = AggregateMessageOrigin; + + fn process_message( + message: &[u8], + origin: Self::Origin, + meter: &mut WeightMeter, + ) -> Result { + let para = match origin { + AggregateMessageOrigin::Ump(UmpQueueId::Para(para)) => para, + }; + xcm_builder::ProcessXcmMessage::< + Junction, + xcm_executor::XcmExecutor, + RuntimeCall, + >::process_message(message, Junction::Parachain(para.into()), meter) + } +} + +impl pallet_message_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Size = u32; + type HeapSize = MessageQueueHeapSize; + type MaxStale = MessageQueueMaxStale; + type ServiceWeight = MessageQueueServiceWeight; + type MessageProcessor = MessageProcessor; + type QueueChangeHandler = (); + type WeightInfo = (); +} + construct_runtime!( pub enum Runtime where Block = Block, @@ -254,8 +289,8 @@ construct_runtime!( System: frame_system::{Pallet, Call, Storage, Config, Event}, Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, ParasOrigin: origin::{Pallet, Origin}, - ParasUmp: ump::{Pallet, Call, Storage, Event}, XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event, Origin}, Uniques: pallet_uniques::{Pallet, Call, Storage, Event}, + MessageQueue: pallet_message_queue::{Pallet, Event}, } ); diff --git a/xcm/xcm-simulator/fuzzer/Cargo.toml b/xcm/xcm-simulator/fuzzer/Cargo.toml index 06d3ae2c43fc..f8cdfa9424cf 100644 --- a/xcm/xcm-simulator/fuzzer/Cargo.toml +++ b/xcm/xcm-simulator/fuzzer/Cargo.toml @@ -14,6 +14,7 @@ scale-info = { version = "2.5.0", features = ["derive"] } frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" } frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-message-queue = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-std = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } @@ -31,6 +32,7 @@ polkadot-parachain = { path = "../../../parachain" } [features] runtime-benchmarks = [ "pallet-xcm/runtime-benchmarks", + "pallet-message-queue/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "frame-support/runtime-benchmarks", ] diff --git a/xcm/xcm-simulator/fuzzer/src/fuzz.rs b/xcm/xcm-simulator/fuzzer/src/fuzz.rs index 42b3ec8d9e08..b23386d79120 100644 --- a/xcm/xcm-simulator/fuzzer/src/fuzz.rs +++ b/xcm/xcm-simulator/fuzzer/src/fuzz.rs @@ -60,7 +60,11 @@ decl_test_parachain! { decl_test_relay_chain! { pub struct Relay { Runtime = relay_chain::Runtime, + RuntimeCall = relay_chain::RuntimeCall, + RuntimeEvent = relay_chain::RuntimeEvent, XcmConfig = relay_chain::XcmConfig, + MessageQueue = relay_chain::MessageQueue, + System = relay_chain::System, new_ext = relay_ext(), } } diff --git a/xcm/xcm-simulator/fuzzer/src/relay_chain.rs b/xcm/xcm-simulator/fuzzer/src/relay_chain.rs index cb001d91f9b5..d6b506af0214 100644 --- a/xcm/xcm-simulator/fuzzer/src/relay_chain.rs +++ b/xcm/xcm-simulator/fuzzer/src/relay_chain.rs @@ -18,8 +18,8 @@ use frame_support::{ construct_runtime, parameter_types, - traits::{Everything, Nothing}, - weights::Weight, + traits::{Everything, Nothing, ProcessMessage, ProcessMessageError}, + weights::{Weight, WeightMeter}, }; use frame_system::EnsureRoot; @@ -27,7 +27,11 @@ use sp_core::{ConstU32, H256}; use sp_runtime::{testing::Header, traits::IdentityLookup, AccountId32}; use polkadot_parachain::primitives::Id as ParaId; -use polkadot_runtime_parachains::{configuration, origin, shared, ump}; +use polkadot_runtime_parachains::{ + configuration, + inclusion::{AggregateMessageOrigin, UmpQueueId}, + origin, shared, +}; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowUnpaidExecutionFrom, ChildParachainAsNative, @@ -122,7 +126,7 @@ type LocalOriginConverter = ( parameter_types! { pub const BaseXcmWeight: Weight = Weight::from_parts(1_000, 1_000); pub KsmPerSecondPerByte: (AssetId, u128, u128) = (Concrete(TokenLocation::get()), 1, 1); - pub const MaxInstructions: u32 = 100; + pub const MaxInstructions: u32 = u32::MAX; pub const MaxAssetsIntoHolding: u32 = 64; } @@ -196,19 +200,54 @@ parameter_types! { pub const FirstMessageFactorPercent: u64 = 100; } -impl ump::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type UmpSink = ump::XcmSink, Runtime>; - type FirstMessageFactorPercent = FirstMessageFactorPercent; - type ExecuteOverweightOrigin = frame_system::EnsureRoot; - type WeightInfo = ump::TestWeightInfo; -} - impl origin::Config for Runtime {} type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; +parameter_types! { + /// Amount of weight that can be spent per block to service messages. + pub MessageQueueServiceWeight: Weight = Weight::from_parts(1_000_000_000, 1_000_000); + pub const MessageQueueHeapSize: u32 = 65_536; + pub const MessageQueueMaxStale: u32 = 16; +} + +/// Message processor to handle any messages that were enqueued into the `MessageQueue` pallet. +pub struct MessageProcessor; +impl ProcessMessage for MessageProcessor { + type Origin = AggregateMessageOrigin; + + fn process_message( + message: &[u8], + origin: Self::Origin, + meter: &mut WeightMeter, + ) -> Result { + let para = match origin { + AggregateMessageOrigin::Ump(UmpQueueId::Para(para)) => para, + }; + xcm_builder::ProcessXcmMessage::< + Junction, + xcm_executor::XcmExecutor, + RuntimeCall, + >::process_message(message, Junction::Parachain(para.into()), meter) + } +} + +impl pallet_message_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Size = u32; + type HeapSize = MessageQueueHeapSize; + type MaxStale = MessageQueueMaxStale; + type ServiceWeight = MessageQueueServiceWeight; + #[cfg(not(feature = "runtime-benchmarks"))] + type MessageProcessor = MessageProcessor; + #[cfg(feature = "runtime-benchmarks")] + type MessageProcessor = + pallet_message_queue::mock_helpers::NoopMessageProcessor; + type QueueChangeHandler = (); + type WeightInfo = (); +} + construct_runtime!( pub enum Runtime where Block = Block, @@ -218,7 +257,7 @@ construct_runtime!( System: frame_system::{Pallet, Call, Storage, Config, Event}, Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, ParasOrigin: origin::{Pallet, Origin}, - ParasUmp: ump::{Pallet, Call, Storage, Event}, XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event, Origin}, + MessageQueue: pallet_message_queue::{Pallet, Event}, } ); diff --git a/xcm/xcm-simulator/src/lib.rs b/xcm/xcm-simulator/src/lib.rs index 6a5b1b49e0ca..a460f0923798 100644 --- a/xcm/xcm-simulator/src/lib.rs +++ b/xcm/xcm-simulator/src/lib.rs @@ -19,7 +19,10 @@ pub use codec::Encode; pub use paste; -pub use frame_support::{traits::Get, weights::Weight}; +pub use frame_support::{ + traits::{EnqueueMessage, Get, ProcessMessage, ProcessMessageError, ServiceQueues}, + weights::{Weight, WeightMeter}, +}; pub use sp_io::{hashing::blake2_256, TestExternalities}; pub use sp_std::{cell::RefCell, collections::vec_deque::VecDeque, marker::PhantomData}; @@ -30,9 +33,10 @@ pub use polkadot_parachain::primitives::{ }; pub use polkadot_runtime_parachains::{ dmp, - ump::{self, MessageId, UmpSink, XcmSink}, + inclusion::{AggregateMessageOrigin, UmpQueueId}, }; pub use xcm::{latest::prelude::*, VersionedXcm}; +pub use xcm_builder::ProcessXcmMessage; pub use xcm_executor::XcmExecutor; pub trait TestExt { @@ -100,7 +104,11 @@ macro_rules! decl_test_relay_chain { ( pub struct $name:ident { Runtime = $runtime:path, + RuntimeCall = $runtime_call:path, + RuntimeEvent = $runtime_event:path, XcmConfig = $xcm_config:path, + MessageQueue = $mq:path, + System = $system:path, new_ext = $new_ext:expr, } ) => { @@ -108,18 +116,37 @@ macro_rules! decl_test_relay_chain { $crate::__impl_ext!($name, $new_ext); - impl $crate::UmpSink for $name { - fn process_upward_message( - origin: $crate::ParaId, + impl $crate::ProcessMessage for $name { + type Origin = $crate::ParaId; + + fn process_message( msg: &[u8], - max_weight: $crate::Weight, - ) -> Result<$crate::Weight, ($crate::MessageId, $crate::Weight)> { - use $crate::{ump::UmpSink, TestExt}; + para: Self::Origin, + meter: &mut $crate::WeightMeter, + ) -> Result { + use $crate::{Weight, AggregateMessageOrigin, UmpQueueId, ServiceQueues, EnqueueMessage}; + use $mq as message_queue; + use $runtime_event as runtime_event; Self::execute_with(|| { - $crate::ump::XcmSink::<$crate::XcmExecutor<$xcm_config>, $runtime>::process_upward_message( - origin, msg, max_weight, - ) + <$mq as EnqueueMessage>::enqueue_message( + msg.try_into().expect("Message too long"), + AggregateMessageOrigin::Ump(UmpQueueId::Para(para.clone())) + ); + + <$system>::reset_events(); + <$mq as ServiceQueues>::service_queues(Weight::MAX); + let events = <$system>::events(); + let event = events.last().expect("There must be at least one event"); + + match &event.event { + runtime_event::MessageQueue( + pallet_message_queue::Event::Processed {origin, ..}) => { + assert_eq!(origin, &AggregateMessageOrigin::Ump(UmpQueueId::Para(para))); + }, + event => panic!("Unexpected event: {:#?}", event), + } + Ok(true) }) } } @@ -286,19 +313,23 @@ macro_rules! decl_test_network { /// Process all messages originating from parachains. fn process_para_messages() -> $crate::XcmResult { - use $crate::{UmpSink, XcmpMessageHandlerT}; + use $crate::{ProcessMessage, XcmpMessageHandlerT}; while let Some((para_id, destination, message)) = $crate::PARA_MESSAGE_BUS.with( |b| b.borrow_mut().pop_front()) { match destination.interior() { $crate::Junctions::Here if destination.parent_count() == 1 => { let encoded = $crate::encode_xcm(message, $crate::MessageKind::Ump); - let r = <$relay_chain>::process_upward_message( - para_id, &encoded[..], - $crate::Weight::MAX, + let r = <$relay_chain>::process_message( + encoded.as_slice(), para_id, + &mut $crate::WeightMeter::max_limit(), ); - if let Err((id, required)) = r { - return Err($crate::XcmError::WeightLimitReached(required)); + match r { + Err($crate::ProcessMessageError::Overweight(required)) => + return Err($crate::XcmError::WeightLimitReached(required)), + // Not really the correct error, but there is no "undecodable". + Err(_) => return Err($crate::XcmError::Unimplemented), + Ok(_) => (), } }, $( @@ -353,7 +384,7 @@ macro_rules! decl_test_network { destination: &mut Option<$crate::MultiLocation>, message: &mut Option<$crate::Xcm<()>>, ) -> $crate::SendResult<($crate::ParaId, $crate::MultiLocation, $crate::Xcm<()>)> { - use $crate::{UmpSink, XcmpMessageHandlerT}; + use $crate::XcmpMessageHandlerT; let d = destination.take().ok_or($crate::SendError::MissingArgument)?; match (d.interior(), d.parent_count()) {