General Message Queue Pallet (#12485)

* The message queue

* Make fully generic

* Refactor

* Docs

* Refactor

* Use iter not slice

* Per-origin queues

* Multi-queue processing

* Introduce MaxReady

* Remove MaxReady in favour of ready ring

* Cleanups

* ReadyRing and tests

* Stale page reaping

* from_components -> from_parts

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Move WeightCounter to sp_weights

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Add MockedWeightInfo

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Deploy to kitchensink

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Use WeightCounter

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Small fixes and logging

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Add service_page

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Typo

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Move service_page below service_queue

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Add service_message

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Use correct weight function

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Overweight execution

* Refactor

* Missing file

* Fix WeightCounter usage in scheduler

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fix peek_index

Take into account that decoding from a mutable slice modifies it.

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Add tests and bench service_page_item

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Add debug_info

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Add no-progress check to service_queues

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Add more benches

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Bound from_message and try_append_message

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Add PageReaped event

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Rename BookStateOf and BookStateFor

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Update tests and remove logging

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Remove redundant per-message origins; add footprint() and sweep_queue()

* Move testing stuff to mock.rs

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Add integration test

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fix no-progress check

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fix debug_info

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fixup merge and tests

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fix footprint tracking

* Introduce

* Formatting

* OverweightEnqueued event, auto-servicing config item

* Update tests and benchmarks

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Clippy

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Add tests

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Provide change handler

* Add missing BookStateFor::insert and call QueueChangeHandler

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Docs

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Update benchmarks and weights

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* More tests...

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Use weight metering functions

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* weightInfo::process_message_payload is gone

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Add defensive_saturating_accrue

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Rename WeightCounter to WeightMeter

Ctr+Shift+H should do the trick.

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Test on_initialize

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Add module docs

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Remove origin from MaxMessageLen

The message origin is not encoded into the heap and does
therefore not influence the max message length anymore.

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Add BoundedVec::as_slice

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Test Page::{from_message, try_append_message}

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fixup docs

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Docs

* Do nothing in sweep_queue if the queue does not exist

... otherwise it inserts default values into the storage.

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Test ring (un)knitting

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Upgrade stress-test

Change the test to not assume that all queued messages will be
processed in the next block but split it over multiple.

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* More tests...

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Beauty fixes

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* clippy

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Rename BoundedVec::as_slice to as_bounded_slice

Conflicts with deref().as_slice() otherwise.

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fix imports

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Remove ReadyRing struct

Was used for testing only. Instead use 'fn assert_ring' which also
check the service head and backlinks.

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Beauty fixes

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fix stale page watermark

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Cleanup

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fix test feature and clippy

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* QueueChanged handler is called correctly

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Update benches

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Abstract testing functions

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* More tests

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Cleanup

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Clippy

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* fmt

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Simplify tests

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Make stuff compile

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Extend overweight execution benchmark

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Remove TODOs

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Test service queue with faulty MessageProcessor

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* fmt

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Update pallet ui tests to 1.65

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* More docs

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Review doc fixes

Co-authored-by: Robert Klotzner <eskimor@users.noreply.github.com>
Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Add weight_limit to extrinsic weight of execute_overweight

* Correctly return unused weight

* Return actual weight consumed in do_execute_overweight

* Review fixes

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Set version 7.0.0-dev

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Make it compile

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Switch message_size to u64

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Switch message_count to u64

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fix benchmarks

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Make CI green

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Docs

* Update tests

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* ".git/.scripts/bench-bot.sh" pallet dev pallet_message_queue

* Dont mention README.md in the Cargo.toml

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Remove reference to readme

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Co-authored-by: parity-processbot <>
Co-authored-by: Robert Klotzner <eskimor@users.noreply.github.com>
Co-authored-by: Keith Yeung <kungfukeith11@gmail.com>
This commit is contained in:
Gavin Wood
2022-12-09 10:38:24 +00:00
committed by GitHub
parent 487ed143df
commit 6f3d1a8143
20 changed files with 3883 additions and 15 deletions
+32 -8
View File
@@ -3117,6 +3117,7 @@ dependencies = [
"pallet-indices",
"pallet-lottery",
"pallet-membership",
"pallet-message-queue",
"pallet-mmr",
"pallet-multisig",
"pallet-nis",
@@ -5322,6 +5323,28 @@ dependencies = [
"sp-std",
]
[[package]]
name = "pallet-message-queue"
version = "7.0.0-dev"
dependencies = [
"frame-benchmarking",
"frame-support",
"frame-system",
"log",
"parity-scale-codec",
"rand 0.8.5",
"rand_distr",
"scale-info",
"serde",
"sp-arithmetic",
"sp-core",
"sp-io",
"sp-runtime",
"sp-std",
"sp-tracing",
"sp-weights",
]
[[package]]
name = "pallet-mmr"
version = "4.0.0-dev"
@@ -5709,6 +5732,7 @@ dependencies = [
"sp-io",
"sp-runtime",
"sp-std",
"sp-weights",
"substrate-test-utils",
]
@@ -8397,9 +8421,9 @@ dependencies = [
[[package]]
name = "scale-info"
version = "2.1.1"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8980cafbe98a7ee7a9cc16b32ebce542c77883f512d83fbf2ddc8f6a85ea74c9"
checksum = "333af15b02563b8182cd863f925bd31ef8fa86a0e095d30c091956057d436153"
dependencies = [
"bitvec",
"cfg-if",
@@ -8411,9 +8435,9 @@ dependencies = [
[[package]]
name = "scale-info-derive"
version = "2.1.1"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4260c630e8a8a33429d1688eff2f163f24c65a4e1b1578ef6b565061336e4b6f"
checksum = "53f56acbd0743d29ffa08f911ab5397def774ad01bab3786804cf6ee057fb5e1"
dependencies = [
"proc-macro-crate",
"proc-macro2",
@@ -8570,9 +8594,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "serde"
version = "1.0.136"
version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b"
dependencies = [
"serde_derive",
]
@@ -8589,9 +8613,9 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.136"
version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c"
dependencies = [
"proc-macro2",
"quote",
+1
View File
@@ -121,6 +121,7 @@ members = [
"frame/offences/benchmarking",
"frame/preimage",
"frame/proxy",
"frame/message-queue",
"frame/nomination-pools",
"frame/nomination-pools/fuzzer",
"frame/nomination-pools/benchmarking",
+4
View File
@@ -75,6 +75,7 @@ pallet-indices = { version = "4.0.0-dev", default-features = false, path = "../.
pallet-identity = { version = "4.0.0-dev", default-features = false, path = "../../../frame/identity" }
pallet-lottery = { version = "4.0.0-dev", default-features = false, path = "../../../frame/lottery" }
pallet-membership = { version = "4.0.0-dev", default-features = false, path = "../../../frame/membership" }
pallet-message-queue = { version = "7.0.0-dev", default-features = false, path = "../../../frame/message-queue" }
pallet-mmr = { version = "4.0.0-dev", default-features = false, path = "../../../frame/merkle-mountain-range" }
pallet-multisig = { version = "4.0.0-dev", default-features = false, path = "../../../frame/multisig" }
pallet-nomination-pools = { version = "1.0.0", default-features = false, path = "../../../frame/nomination-pools"}
@@ -150,6 +151,7 @@ std = [
"sp-inherents/std",
"pallet-lottery/std",
"pallet-membership/std",
"pallet-message-queue/std",
"pallet-mmr/std",
"pallet-multisig/std",
"pallet-nomination-pools/std",
@@ -229,6 +231,7 @@ runtime-benchmarks = [
"pallet-indices/runtime-benchmarks",
"pallet-lottery/runtime-benchmarks",
"pallet-membership/runtime-benchmarks",
"pallet-message-queue/runtime-benchmarks",
"pallet-mmr/runtime-benchmarks",
"pallet-multisig/runtime-benchmarks",
"pallet-nomination-pools-benchmarking/runtime-benchmarks",
@@ -282,6 +285,7 @@ try-runtime = [
"pallet-identity/try-runtime",
"pallet-lottery/try-runtime",
"pallet-membership/try-runtime",
"pallet-message-queue/try-runtime",
"pallet-mmr/try-runtime",
"pallet-multisig/try-runtime",
"pallet-nomination-pools/try-runtime",
+21
View File
@@ -1135,6 +1135,25 @@ impl pallet_bounties::Config for Runtime {
type ChildBountyManager = ChildBounties;
}
parameter_types! {
/// Allocate at most 20% of each block for message processing.
///
/// Is set to 20% since the scheduler can already consume a maximum of 80%.
pub MessageQueueServiceWeight: Option<Weight> = Some(Perbill::from_percent(20) * RuntimeBlockWeights::get().max_block);
}
impl pallet_message_queue::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
/// NOTE: Always set this to `NoopMessageProcessor` for benchmarking.
type MessageProcessor = pallet_message_queue::mock_helpers::NoopMessageProcessor;
type Size = u32;
type QueueChangeHandler = ();
type HeapSize = ConstU32<{ 64 * 1024 }>;
type MaxStale = ConstU32<128>;
type ServiceWeight = MessageQueueServiceWeight;
}
parameter_types! {
pub const ChildBountyValueMinimum: Balance = 1 * DOLLARS;
}
@@ -1699,6 +1718,7 @@ construct_runtime!(
RankedPolls: pallet_referenda::<Instance2>,
RankedCollective: pallet_ranked_collective,
FastUnstake: pallet_fast_unstake,
MessageQueue: pallet_message_queue,
}
);
@@ -1793,6 +1813,7 @@ mod benches {
[pallet_indices, Indices]
[pallet_lottery, Lottery]
[pallet_membership, TechnicalMembership]
[pallet_message_queue, MessageQueue]
[pallet_mmr, Mmr]
[pallet_multisig, Multisig]
[pallet_nomination_pools, NominationPoolsBench::<Runtime>]
+53
View File
@@ -0,0 +1,53 @@
[package]
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2021"
name = "pallet-message-queue"
version = "7.0.0-dev"
license = "Apache-2.0"
homepage = "https://substrate.io"
repository = "https://github.com/paritytech/substrate/"
description = "FRAME pallet to queue and process messages"
[dependencies]
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] }
scale-info = { version = "2.1.2", default-features = false, features = ["derive"] }
serde = { version = "1.0.137", optional = true, features = ["derive"] }
log = { version = "0.4.17", default-features = false }
sp-core = { version = "7.0.0", default-features = false, path = "../../primitives/core" }
sp-io = { version = "7.0.0", default-features = false, path = "../../primitives/io" }
sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" }
sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" }
sp-arithmetic = { version = "6.0.0", default-features = false, path = "../../primitives/arithmetic" }
sp-weights = { version = "4.0.0", default-features = false, path = "../../primitives/weights" }
frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" }
frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" }
frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" }
[dev-dependencies]
sp-tracing = { version = "6.0.0", path = "../../primitives/tracing" }
rand = "0.8.5"
rand_distr = "0.4.3"
[features]
default = ["std"]
std = [
"codec/std",
"scale-info/std",
"sp-core/std",
"sp-io/std",
"sp-runtime/std",
"sp-std/std",
"sp-arithmetic/std",
"sp-weights/std",
"frame-benchmarking?/std",
"frame-support/std",
"frame-system/std",
]
runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
]
try-runtime = ["frame-support/try-runtime"]
@@ -0,0 +1,204 @@
// Copyright 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 <http://www.gnu.org/licenses/>.
//! Benchmarking for the message queue pallet.
#![cfg(feature = "runtime-benchmarks")]
#![allow(unused_assignments)] // Needed for `ready_ring_knit`.
use super::{mock_helpers::*, Pallet as MessageQueue, *};
use frame_benchmarking::{benchmarks, whitelisted_caller};
use frame_support::traits::Get;
use frame_system::RawOrigin;
use sp_std::prelude::*;
benchmarks! {
where_clause {
where
// NOTE: We need to generate multiple origins, therefore Origin is `From<u32>`. The
// `PartialEq` is for asserting the outcome of the ring (un)knitting and *could* be
// removed if really necessary.
<<T as Config>::MessageProcessor as ProcessMessage>::Origin: From<u32> + PartialEq,
<T as Config>::Size: From<u32>,
}
// Worst case path of `ready_ring_knit`.
ready_ring_knit {
let mid: MessageOriginOf::<T> = 1.into();
build_ring::<T>(&[0.into(), mid.clone(), 2.into()]);
unknit::<T>(&mid);
assert_ring::<T>(&[0.into(), 2.into()]);
let mut neighbours = None;
}: {
neighbours = MessageQueue::<T>::ready_ring_knit(&mid).ok();
} verify {
// The neighbours needs to be modified manually.
BookStateFor::<T>::mutate(&mid, |b| { b.ready_neighbours = neighbours });
assert_ring::<T>(&[0.into(), 2.into(), mid]);
}
// Worst case path of `ready_ring_unknit`.
ready_ring_unknit {
build_ring::<T>(&[0.into(), 1.into(), 2.into()]);
assert_ring::<T>(&[0.into(), 1.into(), 2.into()]);
let o: MessageOriginOf::<T> = 0.into();
let neighbours = BookStateFor::<T>::get(&o).ready_neighbours.unwrap();
}: {
MessageQueue::<T>::ready_ring_unknit(&o, neighbours);
} verify {
assert_ring::<T>(&[1.into(), 2.into()]);
}
// `service_queues` without any queue processing.
service_queue_base {
}: {
MessageQueue::<T>::service_queue(0.into(), &mut WeightMeter::max_limit(), Weight::MAX)
}
// `service_page` without any message processing but with page completion.
service_page_base_completion {
let origin: MessageOriginOf<T> = 0.into();
let page = PageOf::<T>::default();
Pages::<T>::insert(&origin, 0, &page);
let mut book_state = single_page_book::<T>();
let mut meter = WeightMeter::max_limit();
let limit = Weight::MAX;
}: {
MessageQueue::<T>::service_page(&origin, &mut book_state, &mut meter, limit)
}
// `service_page` without any message processing and without page completion.
service_page_base_no_completion {
let origin: MessageOriginOf<T> = 0.into();
let mut page = PageOf::<T>::default();
// Mock the storage such that `is_complete` returns `false` but `peek_first` returns `None`.
page.first = 1.into();
page.remaining = 1.into();
Pages::<T>::insert(&origin, 0, &page);
let mut book_state = single_page_book::<T>();
let mut meter = WeightMeter::max_limit();
let limit = Weight::MAX;
}: {
MessageQueue::<T>::service_page(&origin, &mut book_state, &mut meter, limit)
}
// Processing a single message from a page.
service_page_item {
let msg = vec![1u8; MaxMessageLenOf::<T>::get() as usize];
let mut page = page::<T>(&msg.clone());
let mut book = book_for::<T>(&page);
assert!(page.peek_first().is_some(), "There is one message");
let mut weight = WeightMeter::max_limit();
}: {
let status = MessageQueue::<T>::service_page_item(&0u32.into(), 0, &mut book, &mut page, &mut weight, Weight::MAX);
assert_eq!(status, ItemExecutionStatus::Executed(true));
} verify {
// Check that it was processed.
assert_last_event::<T>(Event::Processed {
hash: T::Hashing::hash(&msg), origin: 0.into(),
weight_used: 1.into_weight(), success: true
}.into());
let (_, processed, _) = page.peek_index(0).unwrap();
assert!(processed);
assert_eq!(book.message_count, 0);
}
// Worst case for calling `bump_service_head`.
bump_service_head {
setup_bump_service_head::<T>(0.into(), 10.into());
let mut weight = WeightMeter::max_limit();
}: {
MessageQueue::<T>::bump_service_head(&mut weight);
} verify {
assert_eq!(ServiceHead::<T>::get().unwrap(), 10u32.into());
assert_eq!(weight.consumed, T::WeightInfo::bump_service_head());
}
reap_page {
// Mock the storage to get a *cullable* but not *reapable* page.
let origin: MessageOriginOf<T> = 0.into();
let mut book = single_page_book::<T>();
let (page, msgs) = full_page::<T>();
for p in 0 .. T::MaxStale::get() * T::MaxStale::get() {
if p == 0 {
Pages::<T>::insert(&origin, p, &page);
}
book.end += 1;
book.count += 1;
book.message_count += msgs as u64;
book.size += page.remaining_size.into() as u64;
}
book.begin = book.end - T::MaxStale::get();
BookStateFor::<T>::insert(&origin, &book);
assert!(Pages::<T>::contains_key(&origin, 0));
}: _(RawOrigin::Signed(whitelisted_caller()), 0u32.into(), 0)
verify {
assert_last_event::<T>(Event::PageReaped{ origin: 0.into(), index: 0 }.into());
assert!(!Pages::<T>::contains_key(&origin, 0));
}
// Worst case for `execute_overweight` where the page is removed as completed.
//
// The worst case occurs when executing the last message in a page of which all are skipped since it is using `peek_index` which has linear complexities.
execute_overweight_page_removed {
let origin: MessageOriginOf<T> = 0.into();
let (mut page, msgs) = full_page::<T>();
// Skip all messages.
for _ in 1..msgs {
page.skip_first(true);
}
page.skip_first(false);
let book = book_for::<T>(&page);
Pages::<T>::insert(&origin, 0, &page);
BookStateFor::<T>::insert(&origin, &book);
}: {
MessageQueue::<T>::execute_overweight(RawOrigin::Signed(whitelisted_caller()).into(), 0u32.into(), 0u32, ((msgs - 1) as u32).into(), Weight::MAX).unwrap()
}
verify {
assert_last_event::<T>(Event::Processed {
hash: T::Hashing::hash(&((msgs - 1) as u32).encode()), origin: 0.into(),
weight_used: Weight::from_parts(1, 1), success: true
}.into());
assert!(!Pages::<T>::contains_key(&origin, 0), "Page must be removed");
}
// Worst case for `execute_overweight` where the page is updated.
execute_overweight_page_updated {
let origin: MessageOriginOf<T> = 0.into();
let (mut page, msgs) = full_page::<T>();
// Skip all messages.
for _ in 0..msgs {
page.skip_first(false);
}
let book = book_for::<T>(&page);
Pages::<T>::insert(&origin, 0, &page);
BookStateFor::<T>::insert(&origin, &book);
}: {
MessageQueue::<T>::execute_overweight(RawOrigin::Signed(whitelisted_caller()).into(), 0u32.into(), 0u32, ((msgs - 1) as u32).into(), Weight::MAX).unwrap()
}
verify {
assert_last_event::<T>(Event::Processed {
hash: T::Hashing::hash(&((msgs - 1) as u32).encode()), origin: 0.into(),
weight_used: Weight::from_parts(1, 1), success: true
}.into());
assert!(Pages::<T>::contains_key(&origin, 0), "Page must be updated");
}
impl_benchmark_test_suite!(MessageQueue, crate::mock::new_test_ext::<crate::integration_test::Test>(), crate::integration_test::Test);
}
@@ -0,0 +1,224 @@
// Copyright 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 <http://www.gnu.org/licenses/>.
//! Stress tests pallet-message-queue. Defines its own runtime config to use larger constants for
//! `HeapSize` and `MaxStale`.
#![cfg(test)]
use crate::{
mock::{
new_test_ext, CountingMessageProcessor, IntoWeight, MockedWeightInfo, NumMessagesProcessed,
},
*,
};
use crate as pallet_message_queue;
use frame_support::{
parameter_types,
traits::{ConstU32, ConstU64},
};
use rand::{rngs::StdRng, Rng, SeedableRng};
use rand_distr::Pareto;
use sp_core::H256;
use sp_runtime::{
testing::Header,
traits::{BlakeTwo256, IdentityLookup},
};
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
type Block = frame_system::mocking::MockBlock<Test>;
frame_support::construct_runtime!(
pub enum Test where
Block = Block,
NodeBlock = Block,
UncheckedExtrinsic = UncheckedExtrinsic,
{
System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
MessageQueue: pallet_message_queue::{Pallet, Call, Storage, Event<T>},
}
);
parameter_types! {
pub BlockWeights: frame_system::limits::BlockWeights =
frame_system::limits::BlockWeights::simple_max(frame_support::weights::Weight::from_ref_time(1024));
}
impl frame_system::Config for Test {
type BaseCallFilter = frame_support::traits::Everything;
type BlockWeights = ();
type BlockLength = ();
type DbWeight = ();
type RuntimeOrigin = RuntimeOrigin;
type Index = u64;
type BlockNumber = u64;
type Hash = H256;
type RuntimeCall = RuntimeCall;
type Hashing = BlakeTwo256;
type AccountId = u64;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header;
type RuntimeEvent = RuntimeEvent;
type BlockHashCount = ConstU64<250>;
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = ();
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = ConstU32<16>;
}
parameter_types! {
pub const HeapSize: u32 = 32 * 1024;
pub const MaxStale: u32 = 32;
pub static ServiceWeight: Option<Weight> = Some(Weight::from_parts(100, 100));
}
impl Config for Test {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = MockedWeightInfo;
type MessageProcessor = CountingMessageProcessor;
type Size = u32;
type QueueChangeHandler = ();
type HeapSize = HeapSize;
type MaxStale = MaxStale;
type ServiceWeight = ServiceWeight;
}
/// Simulates heavy usage by enqueueing and processing large amounts of messages.
///
/// Best to run with `-r`, `RUST_LOG=info` and `RUSTFLAGS='-Cdebug-assertions=y'`.
///
/// # Example output
///
/// ```pre
/// Enqueued 1189 messages across 176 queues. Payload 46.97 KiB
/// Processing 772 of 1189 messages
/// Enqueued 9270 messages across 1559 queues. Payload 131.85 KiB
/// Processing 6262 of 9687 messages
/// Enqueued 5025 messages across 1225 queues. Payload 100.23 KiB
/// Processing 1739 of 8450 messages
/// Enqueued 42061 messages across 6357 queues. Payload 536.29 KiB
/// Processing 11675 of 48772 messages
/// Enqueued 20253 messages across 2420 queues. Payload 288.34 KiB
/// Processing 28711 of 57350 messages
/// Processing all remaining 28639 messages
/// ```
#[test]
#[ignore] // Only run in the CI.
fn stress_test_enqueue_and_service() {
let blocks = 20;
let max_queues = 10_000;
let max_messages_per_queue = 10_000;
let max_msg_len = MaxMessageLenOf::<Test>::get();
let mut rng = StdRng::seed_from_u64(42);
new_test_ext::<Test>().execute_with(|| {
let mut msgs_remaining = 0;
for _ in 0..blocks {
// Start by enqueuing a large number of messages.
let (enqueued, _) =
enqueue_messages(max_queues, max_messages_per_queue, max_msg_len, &mut rng);
msgs_remaining += enqueued;
// Pick a fraction of all messages currently in queue and process them.
let processed = rng.gen_range(1..=msgs_remaining);
log::info!("Processing {} of all messages {}", processed, msgs_remaining);
process_messages(processed); // This also advances the block.
msgs_remaining -= processed;
}
log::info!("Processing all remaining {} messages", msgs_remaining);
process_messages(msgs_remaining);
post_conditions();
});
}
/// Enqueue a random number of random messages into a random number of queues.
fn enqueue_messages(
max_queues: u32,
max_per_queue: u32,
max_msg_len: u32,
rng: &mut StdRng,
) -> (u32, usize) {
let num_queues = rng.gen_range(1..max_queues);
let mut num_messages = 0;
let mut total_msg_len = 0;
for origin in 0..num_queues {
let num_messages_per_queue =
(rng.sample(Pareto::new(1.0, 1.1).unwrap()) as u32).min(max_per_queue);
for m in 0..num_messages_per_queue {
let mut message = format!("{}:{}", &origin, &m).into_bytes();
let msg_len = (rng.sample(Pareto::new(1.0, 1.0).unwrap()) as u32)
.clamp(message.len() as u32, max_msg_len);
message.resize(msg_len as usize, 0);
MessageQueue::enqueue_message(
BoundedSlice::defensive_truncate_from(&message),
origin.into(),
);
total_msg_len += msg_len;
}
num_messages += num_messages_per_queue;
}
log::info!(
"Enqueued {} messages across {} queues. Payload {:.2} KiB",
num_messages,
num_queues,
total_msg_len as f64 / 1024.0
);
(num_messages, total_msg_len as usize)
}
/// Process the number of messages.
fn process_messages(num_msgs: u32) {
let weight = (num_msgs as u64).into_weight();
ServiceWeight::set(Some(weight));
let consumed = next_block();
assert_eq!(consumed, weight, "\n{}", MessageQueue::debug_info());
assert_eq!(NumMessagesProcessed::take(), num_msgs as usize);
}
/// Returns the weight consumed by `MessageQueue::on_initialize()`.
fn next_block() -> Weight {
MessageQueue::on_finalize(System::block_number());
System::on_finalize(System::block_number());
System::set_block_number(System::block_number() + 1);
System::on_initialize(System::block_number());
MessageQueue::on_initialize(System::block_number())
}
/// Assert that the pallet is in the expected post state.
fn post_conditions() {
// All queues are empty.
for (_, book) in BookStateFor::<Test>::iter() {
assert!(book.end >= book.begin);
assert_eq!(book.count, 0);
assert_eq!(book.size, 0);
assert_eq!(book.message_count, 0);
assert!(book.ready_neighbours.is_none());
}
// No pages remain.
assert_eq!(Pages::<Test>::iter().count(), 0);
// Service head is gone.
assert!(ServiceHead::<Test>::get().is_none());
// This still works fine.
assert_eq!(MessageQueue::service_queues(Weight::MAX), Weight::zero(), "Nothing left");
next_block();
}
File diff suppressed because it is too large Load Diff
+312
View File
@@ -0,0 +1,312 @@
// Copyright 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 <http://www.gnu.org/licenses/>.
#![cfg(test)]
pub use super::mock_helpers::*;
use super::*;
use crate as pallet_message_queue;
use frame_support::{
parameter_types,
traits::{ConstU32, ConstU64},
};
use sp_core::H256;
use sp_runtime::{
testing::Header,
traits::{BlakeTwo256, IdentityLookup},
};
use sp_std::collections::btree_map::BTreeMap;
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
type Block = frame_system::mocking::MockBlock<Test>;
frame_support::construct_runtime!(
pub enum Test where
Block = Block,
NodeBlock = Block,
UncheckedExtrinsic = UncheckedExtrinsic,
{
System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
MessageQueue: pallet_message_queue::{Pallet, Call, Storage, Event<T>},
}
);
parameter_types! {
pub BlockWeights: frame_system::limits::BlockWeights =
frame_system::limits::BlockWeights::simple_max(frame_support::weights::Weight::from_ref_time(1024));
}
impl frame_system::Config for Test {
type BaseCallFilter = frame_support::traits::Everything;
type BlockWeights = ();
type BlockLength = ();
type DbWeight = ();
type RuntimeOrigin = RuntimeOrigin;
type Index = u64;
type BlockNumber = u64;
type Hash = H256;
type RuntimeCall = RuntimeCall;
type Hashing = BlakeTwo256;
type AccountId = u64;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header;
type RuntimeEvent = RuntimeEvent;
type BlockHashCount = ConstU64<250>;
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = ();
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = ConstU32<16>;
}
parameter_types! {
pub const HeapSize: u32 = 24;
pub const MaxStale: u32 = 2;
pub const ServiceWeight: Option<Weight> = Some(Weight::from_parts(10, 10));
}
impl Config for Test {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = MockedWeightInfo;
type MessageProcessor = RecordingMessageProcessor;
type Size = u32;
type QueueChangeHandler = RecordingQueueChangeHandler;
type HeapSize = HeapSize;
type MaxStale = MaxStale;
type ServiceWeight = ServiceWeight;
}
/// Mocked `WeightInfo` impl with allows to set the weight per call.
pub struct MockedWeightInfo;
parameter_types! {
/// Storage for `MockedWeightInfo`, do not use directly.
pub static WeightForCall: BTreeMap<String, Weight> = Default::default();
}
/// Set the return value for a function from the `WeightInfo` trait.
impl MockedWeightInfo {
/// Set the weight of a specific weight function.
pub fn set_weight<T: Config>(call_name: &str, weight: Weight) {
let mut calls = WeightForCall::get();
calls.insert(call_name.into(), weight);
WeightForCall::set(calls);
}
}
impl crate::weights::WeightInfo for MockedWeightInfo {
fn reap_page() -> Weight {
WeightForCall::get().get("reap_page").copied().unwrap_or_default()
}
fn execute_overweight_page_updated() -> Weight {
WeightForCall::get()
.get("execute_overweight_page_updated")
.copied()
.unwrap_or_default()
}
fn execute_overweight_page_removed() -> Weight {
WeightForCall::get()
.get("execute_overweight_page_removed")
.copied()
.unwrap_or_default()
}
fn service_page_base_completion() -> Weight {
WeightForCall::get()
.get("service_page_base_completion")
.copied()
.unwrap_or_default()
}
fn service_page_base_no_completion() -> Weight {
WeightForCall::get()
.get("service_page_base_no_completion")
.copied()
.unwrap_or_default()
}
fn service_queue_base() -> Weight {
WeightForCall::get().get("service_queue_base").copied().unwrap_or_default()
}
fn bump_service_head() -> Weight {
WeightForCall::get().get("bump_service_head").copied().unwrap_or_default()
}
fn service_page_item() -> Weight {
WeightForCall::get().get("service_page_item").copied().unwrap_or_default()
}
fn ready_ring_knit() -> Weight {
WeightForCall::get().get("ready_ring_knit").copied().unwrap_or_default()
}
fn ready_ring_unknit() -> Weight {
WeightForCall::get().get("ready_ring_unknit").copied().unwrap_or_default()
}
}
parameter_types! {
pub static MessagesProcessed: Vec<(Vec<u8>, MessageOrigin)> = vec![];
}
/// A message processor which records all processed messages into [`MessagesProcessed`].
pub struct RecordingMessageProcessor;
impl ProcessMessage for RecordingMessageProcessor {
/// The transport from where a message originates.
type Origin = MessageOrigin;
/// Process the given message, using no more than `weight_limit` in weight to do so.
///
/// Consumes exactly `n` weight of all components if it starts `weight=n` and `1` otherwise.
/// Errors if given the `weight_limit` is insufficient to process the message or if the message
/// is `badformat`, `corrupt` or `unsupported` with the respective error.
fn process_message(
message: &[u8],
origin: Self::Origin,
weight_limit: Weight,
) -> Result<(bool, Weight), ProcessMessageError> {
processing_message(message)?;
let weight = if message.starts_with(&b"weight="[..]) {
let mut w: u64 = 0;
for &c in &message[7..] {
if (b'0'..=b'9').contains(&c) {
w = w * 10 + (c - b'0') as u64;
} else {
break
}
}
w
} else {
1
};
let weight = Weight::from_parts(weight, weight);
if weight.all_lte(weight_limit) {
let mut m = MessagesProcessed::get();
m.push((message.to_vec(), origin));
MessagesProcessed::set(m);
Ok((true, weight))
} else {
Err(ProcessMessageError::Overweight(weight))
}
}
}
/// Processed a mocked message. Messages that end with `badformat`, `corrupt` or `unsupported` will
/// fail with the respective error.
fn processing_message(msg: &[u8]) -> Result<(), ProcessMessageError> {
let msg = String::from_utf8_lossy(msg);
if msg.ends_with("badformat") {
Err(ProcessMessageError::BadFormat)
} else if msg.ends_with("corrupt") {
Err(ProcessMessageError::Corrupt)
} else if msg.ends_with("unsupported") {
Err(ProcessMessageError::Unsupported)
} else {
Ok(())
}
}
parameter_types! {
pub static NumMessagesProcessed: usize = 0;
pub static NumMessagesErrored: usize = 0;
}
/// Similar to [`RecordingMessageProcessor`] but only counts the number of messages processed and
/// does always consume one weight per message.
///
/// The [`RecordingMessageProcessor`] is a bit too slow for the integration tests.
pub struct CountingMessageProcessor;
impl ProcessMessage for CountingMessageProcessor {
type Origin = MessageOrigin;
fn process_message(
message: &[u8],
_origin: Self::Origin,
weight_limit: Weight,
) -> Result<(bool, Weight), ProcessMessageError> {
if let Err(e) = processing_message(message) {
NumMessagesErrored::set(NumMessagesErrored::get() + 1);
return Err(e)
}
let weight = Weight::from_parts(1, 1);
if weight.all_lte(weight_limit) {
NumMessagesProcessed::set(NumMessagesProcessed::get() + 1);
Ok((true, weight))
} else {
Err(ProcessMessageError::Overweight(weight))
}
}
}
parameter_types! {
/// Storage for `RecordingQueueChangeHandler`, do not use directly.
pub static QueueChanges: Vec<(MessageOrigin, u64, u64)> = vec![];
}
/// Records all queue changes into [`QueueChanges`].
pub struct RecordingQueueChangeHandler;
impl OnQueueChanged<MessageOrigin> for RecordingQueueChangeHandler {
fn on_queue_changed(id: MessageOrigin, items_count: u64, items_size: u64) {
QueueChanges::mutate(|cs| cs.push((id, items_count, items_size)));
}
}
/// Create new test externalities.
///
/// Is generic since it is used by the unit test, integration tests and benchmarks.
pub fn new_test_ext<T: Config>() -> sp_io::TestExternalities
where
<T as frame_system::Config>::BlockNumber: From<u32>,
{
sp_tracing::try_init_simple();
WeightForCall::take();
QueueChanges::take();
NumMessagesErrored::take();
let t = frame_system::GenesisConfig::default().build_storage::<T>().unwrap();
let mut ext = sp_io::TestExternalities::new(t);
ext.execute_with(|| frame_system::Pallet::<T>::set_block_number(1.into()));
ext
}
/// Set the weight of a specific weight function.
pub fn set_weight(name: &str, w: Weight) {
MockedWeightInfo::set_weight::<Test>(name, w);
}
/// Assert that exactly these pages are present. Assumes `Here` origin.
pub fn assert_pages(indices: &[u32]) {
assert_eq!(Pages::<Test>::iter().count(), indices.len());
for i in indices {
assert!(Pages::<Test>::contains_key(MessageOrigin::Here, i));
}
}
/// Build a ring with three queues: `Here`, `There` and `Everywhere(0)`.
pub fn build_triple_ring() {
use MessageOrigin::*;
build_ring::<Test>(&[Here, There, Everywhere(0)])
}
/// Shim to get rid of the annoying `::<Test>` everywhere.
pub fn assert_ring(queues: &[MessageOrigin]) {
super::mock_helpers::assert_ring::<Test>(queues);
}
pub fn knit(queue: &MessageOrigin) {
super::mock_helpers::knit::<Test>(queue);
}
pub fn unknit(queue: &MessageOrigin) {
super::mock_helpers::unknit::<Test>(queue);
}
@@ -0,0 +1,185 @@
// Copyright 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 <http://www.gnu.org/licenses/>.
//! Std setup helpers for testing and benchmarking.
//!
//! Cannot be put into mock.rs since benchmarks require no-std and mock.rs is std.
use crate::*;
use frame_support::traits::Defensive;
/// Converts `Self` into a `Weight` by using `Self` for all components.
pub trait IntoWeight {
fn into_weight(self) -> Weight;
}
impl IntoWeight for u64 {
fn into_weight(self) -> Weight {
Weight::from_parts(self, self)
}
}
/// Mocked message origin for testing.
#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, MaxEncodedLen, TypeInfo, Debug)]
pub enum MessageOrigin {
Here,
There,
Everywhere(u32),
}
impl From<u32> for MessageOrigin {
fn from(i: u32) -> Self {
Self::Everywhere(i)
}
}
/// Processes any message and consumes (1, 1) weight per message.
pub struct NoopMessageProcessor;
impl ProcessMessage for NoopMessageProcessor {
type Origin = MessageOrigin;
fn process_message(
_message: &[u8],
_origin: Self::Origin,
weight_limit: Weight,
) -> Result<(bool, Weight), ProcessMessageError> {
let weight = Weight::from_parts(1, 1);
if weight.all_lte(weight_limit) {
Ok((true, weight))
} else {
Err(ProcessMessageError::Overweight(weight))
}
}
}
/// Create a message from the given data.
pub fn msg<N: Get<u32>>(x: &'static str) -> BoundedSlice<u8, N> {
BoundedSlice::defensive_truncate_from(x.as_bytes())
}
pub fn vmsg(x: &'static str) -> Vec<u8> {
x.as_bytes().to_vec()
}
/// Create a page from a single message.
pub fn page<T: Config>(msg: &[u8]) -> PageOf<T> {
PageOf::<T>::from_message::<T>(msg.try_into().unwrap())
}
pub fn single_page_book<T: Config>() -> BookStateOf<T> {
BookState { begin: 0, end: 1, count: 1, ready_neighbours: None, message_count: 0, size: 0 }
}
pub fn empty_book<T: Config>() -> BookStateOf<T> {
BookState { begin: 0, end: 1, count: 1, ready_neighbours: None, message_count: 0, size: 0 }
}
/// Returns a full page of messages with their index as payload and the number of messages.
pub fn full_page<T: Config>() -> (PageOf<T>, usize) {
let mut msgs = 0;
let mut page = PageOf::<T>::default();
for i in 0..u32::MAX {
let r = i.using_encoded(|d| page.try_append_message::<T>(d.try_into().unwrap()));
if r.is_err() {
break
} else {
msgs += 1;
}
}
assert!(msgs > 0, "page must hold at least one message");
(page, msgs)
}
/// Returns a page filled with empty messages and the number of messages.
pub fn book_for<T: Config>(page: &PageOf<T>) -> BookStateOf<T> {
BookState {
count: 1,
begin: 0,
end: 1,
ready_neighbours: None,
message_count: page.remaining.into() as u64,
size: page.remaining_size.into() as u64,
}
}
/// Assert the last event that was emitted.
#[cfg(any(feature = "std", feature = "runtime-benchmarks", test))]
pub fn assert_last_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
assert!(
!frame_system::Pallet::<T>::block_number().is_zero(),
"The genesis block has n o events"
);
frame_system::Pallet::<T>::assert_last_event(generic_event.into());
}
/// Provide a setup for `bump_service_head`.
pub fn setup_bump_service_head<T: Config>(
current: <<T as Config>::MessageProcessor as ProcessMessage>::Origin,
next: <<T as Config>::MessageProcessor as ProcessMessage>::Origin,
) {
let mut book = single_page_book::<T>();
book.ready_neighbours = Some(Neighbours::<MessageOriginOf<T>> { prev: next.clone(), next });
ServiceHead::<T>::put(&current);
BookStateFor::<T>::insert(&current, &book);
}
/// Knit a queue into the ready-ring and write it back to storage.
pub fn knit<T: Config>(o: &<<T as Config>::MessageProcessor as ProcessMessage>::Origin) {
let mut b = BookStateFor::<T>::get(o);
b.ready_neighbours = crate::Pallet::<T>::ready_ring_knit(o).ok().defensive();
BookStateFor::<T>::insert(o, b);
}
/// Unknit a queue into the ready-ring and write it back to storage.
pub fn unknit<T: Config>(o: &<<T as Config>::MessageProcessor as ProcessMessage>::Origin) {
let mut b = BookStateFor::<T>::get(o);
crate::Pallet::<T>::ready_ring_unknit(o, b.ready_neighbours.unwrap());
b.ready_neighbours = None;
BookStateFor::<T>::insert(o, b);
}
/// Build a ring with three queues: `Here`, `There` and `Everywhere(0)`.
pub fn build_ring<T: Config>(
queues: &[<<T as Config>::MessageProcessor as ProcessMessage>::Origin],
) {
for queue in queues {
BookStateFor::<T>::insert(queue, empty_book::<T>());
}
for queue in queues {
knit::<T>(queue);
}
assert_ring::<T>(queues);
}
/// Check that the Ready Ring consists of `queues` in that exact order.
///
/// Also check that all backlinks are valid and that the first element is the service head.
pub fn assert_ring<T: Config>(
queues: &[<<T as Config>::MessageProcessor as ProcessMessage>::Origin],
) {
for (i, origin) in queues.iter().enumerate() {
let book = BookStateFor::<T>::get(origin);
assert_eq!(
book.ready_neighbours,
Some(Neighbours {
prev: queues[(i + queues.len() - 1) % queues.len()].clone(),
next: queues[(i + 1) % queues.len()].clone(),
})
);
}
assert_eq!(ServiceHead::<T>::get(), queues.first().cloned());
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,216 @@
// This file is part of Substrate.
// Copyright (C) 2022 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Autogenerated weights for pallet_message_queue
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2022-12-08, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz`
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024
// Executed Command:
// /home/benchbot/cargo_target_dir/production/substrate
// benchmark
// pallet
// --steps=50
// --repeat=20
// --extrinsic=*
// --execution=wasm
// --wasm-execution=compiled
// --heap-pages=4096
// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json
// --pallet=pallet_message_queue
// --chain=dev
// --header=./HEADER-APACHE2
// --output=./frame/message-queue/src/weights.rs
// --template=./.maintain/frame-weight-template.hbs
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(unused_parens)]
#![allow(unused_imports)]
use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
use sp_std::marker::PhantomData;
/// Weight functions needed for pallet_message_queue.
pub trait WeightInfo {
fn ready_ring_knit() -> Weight;
fn ready_ring_unknit() -> Weight;
fn service_queue_base() -> Weight;
fn service_page_base_completion() -> Weight;
fn service_page_base_no_completion() -> Weight;
fn service_page_item() -> Weight;
fn bump_service_head() -> Weight;
fn reap_page() -> Weight;
fn execute_overweight_page_removed() -> Weight;
fn execute_overweight_page_updated() -> Weight;
}
/// Weights for pallet_message_queue using the Substrate node and recommended hardware.
pub struct SubstrateWeight<T>(PhantomData<T>);
impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Storage: MessageQueue ServiceHead (r:1 w:0)
// Storage: MessageQueue BookStateFor (r:2 w:2)
fn ready_ring_knit() -> Weight {
// Minimum execution time: 12_330 nanoseconds.
Weight::from_ref_time(12_711_000)
.saturating_add(T::DbWeight::get().reads(3))
.saturating_add(T::DbWeight::get().writes(2))
}
// Storage: MessageQueue BookStateFor (r:2 w:2)
// Storage: MessageQueue ServiceHead (r:1 w:1)
fn ready_ring_unknit() -> Weight {
// Minimum execution time: 12_322 nanoseconds.
Weight::from_ref_time(12_560_000)
.saturating_add(T::DbWeight::get().reads(3))
.saturating_add(T::DbWeight::get().writes(3))
}
// Storage: MessageQueue BookStateFor (r:1 w:1)
fn service_queue_base() -> Weight {
// Minimum execution time: 4_652 nanoseconds.
Weight::from_ref_time(4_848_000)
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1))
}
// Storage: MessageQueue Pages (r:1 w:1)
fn service_page_base_completion() -> Weight {
// Minimum execution time: 7_115 nanoseconds.
Weight::from_ref_time(7_407_000)
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1))
}
// Storage: MessageQueue Pages (r:1 w:1)
fn service_page_base_no_completion() -> Weight {
// Minimum execution time: 6_974 nanoseconds.
Weight::from_ref_time(7_200_000)
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1))
}
fn service_page_item() -> Weight {
// Minimum execution time: 79_657 nanoseconds.
Weight::from_ref_time(80_050_000)
}
// Storage: MessageQueue ServiceHead (r:1 w:1)
// Storage: MessageQueue BookStateFor (r:1 w:0)
fn bump_service_head() -> Weight {
// Minimum execution time: 7_598 nanoseconds.
Weight::from_ref_time(8_118_000)
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(1))
}
// Storage: MessageQueue BookStateFor (r:1 w:1)
// Storage: MessageQueue Pages (r:1 w:1)
fn reap_page() -> Weight {
// Minimum execution time: 60_562 nanoseconds.
Weight::from_ref_time(61_430_000)
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
// Storage: MessageQueue BookStateFor (r:1 w:1)
// Storage: MessageQueue Pages (r:1 w:1)
fn execute_overweight_page_removed() -> Weight {
// Minimum execution time: 74_582 nanoseconds.
Weight::from_ref_time(75_445_000)
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
// Storage: MessageQueue BookStateFor (r:1 w:1)
// Storage: MessageQueue Pages (r:1 w:1)
fn execute_overweight_page_updated() -> Weight {
// Minimum execution time: 87_526 nanoseconds.
Weight::from_ref_time(88_055_000)
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
}
// For backwards compatibility and tests
impl WeightInfo for () {
// Storage: MessageQueue ServiceHead (r:1 w:0)
// Storage: MessageQueue BookStateFor (r:2 w:2)
fn ready_ring_knit() -> Weight {
// Minimum execution time: 12_330 nanoseconds.
Weight::from_ref_time(12_711_000)
.saturating_add(RocksDbWeight::get().reads(3))
.saturating_add(RocksDbWeight::get().writes(2))
}
// Storage: MessageQueue BookStateFor (r:2 w:2)
// Storage: MessageQueue ServiceHead (r:1 w:1)
fn ready_ring_unknit() -> Weight {
// Minimum execution time: 12_322 nanoseconds.
Weight::from_ref_time(12_560_000)
.saturating_add(RocksDbWeight::get().reads(3))
.saturating_add(RocksDbWeight::get().writes(3))
}
// Storage: MessageQueue BookStateFor (r:1 w:1)
fn service_queue_base() -> Weight {
// Minimum execution time: 4_652 nanoseconds.
Weight::from_ref_time(4_848_000)
.saturating_add(RocksDbWeight::get().reads(1))
.saturating_add(RocksDbWeight::get().writes(1))
}
// Storage: MessageQueue Pages (r:1 w:1)
fn service_page_base_completion() -> Weight {
// Minimum execution time: 7_115 nanoseconds.
Weight::from_ref_time(7_407_000)
.saturating_add(RocksDbWeight::get().reads(1))
.saturating_add(RocksDbWeight::get().writes(1))
}
// Storage: MessageQueue Pages (r:1 w:1)
fn service_page_base_no_completion() -> Weight {
// Minimum execution time: 6_974 nanoseconds.
Weight::from_ref_time(7_200_000)
.saturating_add(RocksDbWeight::get().reads(1))
.saturating_add(RocksDbWeight::get().writes(1))
}
fn service_page_item() -> Weight {
// Minimum execution time: 79_657 nanoseconds.
Weight::from_ref_time(80_050_000)
}
// Storage: MessageQueue ServiceHead (r:1 w:1)
// Storage: MessageQueue BookStateFor (r:1 w:0)
fn bump_service_head() -> Weight {
// Minimum execution time: 7_598 nanoseconds.
Weight::from_ref_time(8_118_000)
.saturating_add(RocksDbWeight::get().reads(2))
.saturating_add(RocksDbWeight::get().writes(1))
}
// Storage: MessageQueue BookStateFor (r:1 w:1)
// Storage: MessageQueue Pages (r:1 w:1)
fn reap_page() -> Weight {
// Minimum execution time: 60_562 nanoseconds.
Weight::from_ref_time(61_430_000)
.saturating_add(RocksDbWeight::get().reads(2))
.saturating_add(RocksDbWeight::get().writes(2))
}
// Storage: MessageQueue BookStateFor (r:1 w:1)
// Storage: MessageQueue Pages (r:1 w:1)
fn execute_overweight_page_removed() -> Weight {
// Minimum execution time: 74_582 nanoseconds.
Weight::from_ref_time(75_445_000)
.saturating_add(RocksDbWeight::get().reads(2))
.saturating_add(RocksDbWeight::get().writes(2))
}
// Storage: MessageQueue BookStateFor (r:1 w:1)
// Storage: MessageQueue Pages (r:1 w:1)
fn execute_overweight_page_updated() -> Weight {
// Minimum execution time: 87_526 nanoseconds.
Weight::from_ref_time(88_055_000)
.saturating_add(RocksDbWeight::get().reads(2))
.saturating_add(RocksDbWeight::get().writes(2))
}
}
+2
View File
@@ -19,6 +19,7 @@ frame-system = { version = "4.0.0-dev", default-features = false, path = "../sys
sp-io = { version = "7.0.0", default-features = false, path = "../../primitives/io" }
sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" }
sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" }
sp-weights = { version = "4.0.0", default-features = false, path = "../../primitives/weights" }
[dev-dependencies]
pallet-preimage = { version = "4.0.0-dev", path = "../preimage" }
@@ -42,5 +43,6 @@ std = [
"sp-io/std",
"sp-runtime/std",
"sp-std/std",
"sp-weights/std",
]
try-runtime = ["frame-support/try-runtime"]
+2 -1
View File
@@ -73,7 +73,6 @@ use frame_support::{
weights::{Weight, WeightMeter},
};
use frame_system::{self as system};
pub use pallet::*;
use scale_info::TypeInfo;
use sp_io::hashing::blake2_256;
use sp_runtime::{
@@ -81,6 +80,8 @@ use sp_runtime::{
BoundedVec, RuntimeDebug,
};
use sp_std::{borrow::Borrow, cmp::Ordering, marker::PhantomData, prelude::*};
pub use pallet::*;
pub use weights::WeightInfo;
/// Just a simple index for naming period tasks.
+6
View File
@@ -112,6 +112,12 @@ pub use voting::{
mod preimages;
pub use preimages::{Bounded, BoundedInline, FetchResult, Hash, QueryPreimage, StorePreimage};
mod messages;
pub use messages::{
EnqueueMessage, ExecuteOverweightError, Footprint, ProcessMessage, ProcessMessageError,
ServiceQueues,
};
#[cfg(feature = "try-runtime")]
mod try_runtime;
#[cfg(feature = "try-runtime")]
@@ -0,0 +1,202 @@
// This file is part of Substrate.
// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Traits for managing message queuing and handling.
use codec::{Decode, Encode, FullCodec, MaxEncodedLen};
use scale_info::TypeInfo;
use sp_core::{ConstU32, Get, TypedGet};
use sp_runtime::{traits::Convert, BoundedSlice, RuntimeDebug};
use sp_std::{fmt::Debug, marker::PhantomData, prelude::*};
use sp_weights::Weight;
/// Errors that can happen when attempting to process a message with
/// [`ProcessMessage::process_message()`].
#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, TypeInfo, RuntimeDebug)]
pub enum ProcessMessageError {
/// The message data format is unknown (e.g. unrecognised header)
BadFormat,
/// The message data is bad (e.g. decoding returns an error).
Corrupt,
/// The message format is unsupported (e.g. old XCM version).
Unsupported,
/// Message processing was not attempted because it was not certain that the weight limit
/// would be respected. The parameter gives the maximum weight which the message could take
/// to process.
Overweight(Weight),
}
/// Can process messages from a specific origin.
pub trait ProcessMessage {
/// The transport from where a message originates.
type Origin: FullCodec + MaxEncodedLen + Clone + Eq + PartialEq + TypeInfo + Debug;
/// Process the given message, using no more than `weight_limit` in weight to do so.
fn process_message(
message: &[u8],
origin: Self::Origin,
weight_limit: Weight,
) -> Result<(bool, Weight), ProcessMessageError>;
}
/// Errors that can happen when attempting to execute an overweight message with
/// [`ServiceQueues::execute_overweight()`].
#[derive(Eq, PartialEq, RuntimeDebug)]
pub enum ExecuteOverweightError {
/// The referenced message was not found.
NotFound,
/// The available weight was insufficient to execute the message.
InsufficientWeight,
}
/// Can service queues and execute overweight messages.
pub trait ServiceQueues {
/// Addresses a specific overweight message.
type OverweightMessageAddress;
/// Service all message queues in some fair manner.
///
/// - `weight_limit`: The maximum amount of dynamic weight that this call can use.
///
/// Returns the dynamic weight used by this call; is never greater than `weight_limit`.
fn service_queues(weight_limit: Weight) -> Weight;
/// Executes a message that could not be executed by [`Self::service_queues()`] because it was
/// temporarily overweight.
fn execute_overweight(
_weight_limit: Weight,
_address: Self::OverweightMessageAddress,
) -> Result<Weight, ExecuteOverweightError> {
Err(ExecuteOverweightError::NotFound)
}
}
/// The resource footprint of a queue.
#[derive(Default, Copy, Clone, Eq, PartialEq, RuntimeDebug)]
pub struct Footprint {
pub count: u64,
pub size: u64,
}
/// Can enqueue messages for multiple origins.
pub trait EnqueueMessage<Origin: MaxEncodedLen> {
/// The maximal length any enqueued message may have.
type MaxMessageLen: Get<u32>;
/// Enqueue a single `message` from a specific `origin`.
fn enqueue_message(message: BoundedSlice<u8, Self::MaxMessageLen>, origin: Origin);
/// Enqueue multiple `messages` from a specific `origin`.
fn enqueue_messages<'a>(
messages: impl Iterator<Item = BoundedSlice<'a, u8, Self::MaxMessageLen>>,
origin: Origin,
);
/// Any remaining unprocessed messages should happen only lazily, not proactively.
fn sweep_queue(origin: Origin);
/// Return the state footprint of the given queue.
fn footprint(origin: Origin) -> Footprint;
}
impl<Origin: MaxEncodedLen> EnqueueMessage<Origin> for () {
type MaxMessageLen = ConstU32<0>;
fn enqueue_message(_: BoundedSlice<u8, Self::MaxMessageLen>, _: Origin) {}
fn enqueue_messages<'a>(
_: impl Iterator<Item = BoundedSlice<'a, u8, Self::MaxMessageLen>>,
_: Origin,
) {
}
fn sweep_queue(_: Origin) {}
fn footprint(_: Origin) -> Footprint {
Footprint::default()
}
}
/// Transform the origin of an [`EnqueueMessage`] via `C::convert`.
pub struct TransformOrigin<E, O, N, C>(PhantomData<(E, O, N, C)>);
impl<E: EnqueueMessage<O>, O: MaxEncodedLen, N: MaxEncodedLen, C: Convert<N, O>> EnqueueMessage<N>
for TransformOrigin<E, O, N, C>
{
type MaxMessageLen = E::MaxMessageLen;
fn enqueue_message(message: BoundedSlice<u8, Self::MaxMessageLen>, origin: N) {
E::enqueue_message(message, C::convert(origin));
}
fn enqueue_messages<'a>(
messages: impl Iterator<Item = BoundedSlice<'a, u8, Self::MaxMessageLen>>,
origin: N,
) {
E::enqueue_messages(messages, C::convert(origin));
}
fn sweep_queue(origin: N) {
E::sweep_queue(C::convert(origin));
}
fn footprint(origin: N) -> Footprint {
E::footprint(C::convert(origin))
}
}
/// Handles incoming messages for a single origin.
pub trait HandleMessage {
/// The maximal length any enqueued message may have.
type MaxMessageLen: Get<u32>;
/// Enqueue a single `message` with an implied origin.
fn handle_message(message: BoundedSlice<u8, Self::MaxMessageLen>);
/// Enqueue multiple `messages` from an implied origin.
fn handle_messages<'a>(
messages: impl Iterator<Item = BoundedSlice<'a, u8, Self::MaxMessageLen>>,
);
/// Any remaining unprocessed messages should happen only lazily, not proactively.
fn sweep_queue();
/// Return the state footprint of the queue.
fn footprint() -> Footprint;
}
/// Adapter type to transform an [`EnqueueMessage`] with an origin into a [`HandleMessage`] impl.
pub struct EnqueueWithOrigin<E, O>(PhantomData<(E, O)>);
impl<E: EnqueueMessage<O::Type>, O: TypedGet> HandleMessage for EnqueueWithOrigin<E, O>
where
O::Type: MaxEncodedLen,
{
type MaxMessageLen = E::MaxMessageLen;
fn handle_message(message: BoundedSlice<u8, Self::MaxMessageLen>) {
E::enqueue_message(message, O::get());
}
fn handle_messages<'a>(
messages: impl Iterator<Item = BoundedSlice<'a, u8, Self::MaxMessageLen>>,
) {
E::enqueue_messages(messages, O::get());
}
fn sweep_queue() {
E::sweep_queue(O::get());
}
fn footprint() -> Footprint {
E::footprint(O::get())
}
}
@@ -28,7 +28,7 @@ error[E0277]: the trait bound `Bar: EncodeLike` is not satisfied
<&[(T,)] as EncodeLike<BinaryHeap<LikeT>>>
<&[(T,)] as EncodeLike<LinkedList<LikeT>>>
<&[T] as EncodeLike<Vec<U>>>
and 278 others
and 279 others
= note: required for `Bar` to implement `FullEncode`
= note: required for `Bar` to implement `FullCodec`
= note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>` to implement `PartialStorageInfoTrait`
@@ -69,7 +69,7 @@ error[E0277]: the trait bound `Bar: TypeInfo` is not satisfied
(A, B, C, D)
(A, B, C, D, E)
(A, B, C, D, E, F)
and 161 others
and 162 others
= note: required for `Bar` to implement `StaticTypeInfo`
= note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>` to implement `StorageEntryMetadataBuilder`
@@ -103,7 +103,7 @@ error[E0277]: the trait bound `Bar: EncodeLike` is not satisfied
<&[(T,)] as EncodeLike<BinaryHeap<LikeT>>>
<&[(T,)] as EncodeLike<LinkedList<LikeT>>>
<&[T] as EncodeLike<Vec<U>>>
and 278 others
and 279 others
= note: required for `Bar` to implement `FullEncode`
= note: required for `Bar` to implement `FullCodec`
= note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>` to implement `StorageEntryMetadataBuilder`
@@ -28,7 +28,7 @@ error[E0277]: the trait bound `Bar: EncodeLike` is not satisfied
<&[(T,)] as EncodeLike<BinaryHeap<LikeT>>>
<&[(T,)] as EncodeLike<LinkedList<LikeT>>>
<&[T] as EncodeLike<Vec<U>>>
and 278 others
and 279 others
= note: required for `Bar` to implement `FullEncode`
= note: required for `Bar` to implement `FullCodec`
= note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>` to implement `PartialStorageInfoTrait`
@@ -69,7 +69,7 @@ error[E0277]: the trait bound `Bar: TypeInfo` is not satisfied
(A, B, C, D)
(A, B, C, D, E)
(A, B, C, D, E, F)
and 161 others
and 162 others
= note: required for `Bar` to implement `StaticTypeInfo`
= note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>` to implement `StorageEntryMetadataBuilder`
@@ -103,7 +103,7 @@ error[E0277]: the trait bound `Bar: EncodeLike` is not satisfied
<&[(T,)] as EncodeLike<BinaryHeap<LikeT>>>
<&[(T,)] as EncodeLike<LinkedList<LikeT>>>
<&[T] as EncodeLike<Vec<U>>>
and 278 others
and 279 others
= note: required for `Bar` to implement `FullEncode`
= note: required for `Bar` to implement `FullCodec`
= note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>` to implement `StorageEntryMetadataBuilder`
@@ -675,6 +675,13 @@ impl<T, S: Get<u32>> BoundedVec<T, S> {
}
}
impl<T, S> BoundedVec<T, S> {
/// Return a [`BoundedSlice`] with the content and bound of [`Self`].
pub fn as_bounded_slice(&self) -> BoundedSlice<T, S> {
BoundedSlice(&self.0[..], PhantomData::default())
}
}
impl<T, S> Default for BoundedVec<T, S> {
fn default() -> Self {
// the bound cannot be below 0, which is satisfied by an empty vector
@@ -71,6 +71,12 @@ impl WeightMeter {
time.max(pov)
}
/// Consume some weight and defensively fail if it is over the limit. Saturate in any case.
pub fn defensive_saturating_accrue(&mut self, w: Weight) {
self.consumed.saturating_accrue(w);
debug_assert!(self.consumed.all_lte(self.limit), "Weight counter overflow");
}
/// Consume the given weight after checking that it can be consumed. Otherwise do nothing.
pub fn check_accrue(&mut self, w: Weight) -> bool {
self.consumed.checked_add(&w).map_or(false, |test| {