mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 07:41:08 +00:00
Preimage registrar and Scheduler integration (#10356)
* initial idea * more * fix compile * add clear and request logic * improve some docs * Add and implement trait * continuing to improve * refcount type * infallible system preimage upload * fmt * fix requests * Make it simple * Make it simple * Formatting * Initial draft * request when scheduled * Docs * Scheduler good * Scheduler good * Scheduler tests working * Add new files * Missing stuff * Repotting, add weights. * Add some tests to preimage pallet * More tests * Fix benchmarks * preimage benchmarks * All preimage benchmarks * Tidy cargo * Update weights.rs * Allow hash provision in benchmarks * Initial work on new benchmarks for Scheduler * Tests working, refactor looks good * Tests for new Scheduler functionality * Use real weight, make tests work with runtimes without Preimage * Rename * Update benchmarks * Formatting * Formatting * Fix weird formatting * Update frame/preimage/src/lib.rs * Fix try-runtime build * Fixes * Fixes * Update frame/support/src/traits/tokens/currency.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Update frame/support/src/traits/tokens/currency/reservable.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Update frame/support/src/traits/tokens/imbalance.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Update frame/preimage/src/mock.rs Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com> * Update frame/scheduler/src/lib.rs Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com> * Update frame/preimage/src/lib.rs * Fixes * Fixes * Formatting * Fixes * Fixes * cargo run --quiet --release --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_scheduler --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/scheduler/src/weights.rs --template=./.maintain/frame-weight-template.hbs * cargo run --quiet --release --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_preimage --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/preimage/src/weights.rs --template=./.maintain/frame-weight-template.hbs Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com> Co-authored-by: Parity Bot <admin@parity.io>
This commit is contained in:
Generated
+18
@@ -4721,6 +4721,7 @@ dependencies = [
|
||||
"pallet-multisig",
|
||||
"pallet-offences",
|
||||
"pallet-offences-benchmarking",
|
||||
"pallet-preimage",
|
||||
"pallet-proxy",
|
||||
"pallet-randomness-collective-flip",
|
||||
"pallet-recovery",
|
||||
@@ -5912,6 +5913,22 @@ dependencies = [
|
||||
"sp-std",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pallet-preimage"
|
||||
version = "4.0.0-dev"
|
||||
dependencies = [
|
||||
"frame-benchmarking",
|
||||
"frame-support",
|
||||
"frame-system",
|
||||
"pallet-balances",
|
||||
"parity-scale-codec",
|
||||
"scale-info",
|
||||
"sp-core",
|
||||
"sp-io",
|
||||
"sp-runtime",
|
||||
"sp-std",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pallet-proxy"
|
||||
version = "4.0.0-dev"
|
||||
@@ -5967,6 +5984,7 @@ dependencies = [
|
||||
"frame-support",
|
||||
"frame-system",
|
||||
"log 0.4.14",
|
||||
"pallet-preimage",
|
||||
"parity-scale-codec",
|
||||
"scale-info",
|
||||
"sp-core",
|
||||
|
||||
@@ -102,6 +102,7 @@ members = [
|
||||
"frame/nicks",
|
||||
"frame/node-authorization",
|
||||
"frame/offences",
|
||||
"frame/preimage",
|
||||
"frame/proxy",
|
||||
"frame/randomness-collective-flip",
|
||||
"frame/recovery",
|
||||
|
||||
@@ -78,6 +78,7 @@ pallet-mmr = { version = "4.0.0-dev", default-features = false, path = "../../..
|
||||
pallet-multisig = { version = "4.0.0-dev", default-features = false, path = "../../../frame/multisig" }
|
||||
pallet-offences = { version = "4.0.0-dev", default-features = false, path = "../../../frame/offences" }
|
||||
pallet-offences-benchmarking = { version = "4.0.0-dev", path = "../../../frame/offences/benchmarking", default-features = false, optional = true }
|
||||
pallet-preimage = { version = "4.0.0-dev", default-features = false, path = "../../../frame/preimage" }
|
||||
pallet-proxy = { version = "4.0.0-dev", default-features = false, path = "../../../frame/proxy" }
|
||||
pallet-randomness-collective-flip = { version = "4.0.0-dev", default-features = false, path = "../../../frame/randomness-collective-flip" }
|
||||
pallet-recovery = { version = "4.0.0-dev", default-features = false, path = "../../../frame/recovery" }
|
||||
@@ -141,6 +142,7 @@ std = [
|
||||
"node-primitives/std",
|
||||
"sp-offchain/std",
|
||||
"pallet-offences/std",
|
||||
"pallet-preimage/std",
|
||||
"pallet-proxy/std",
|
||||
"sp-core/std",
|
||||
"pallet-randomness-collective-flip/std",
|
||||
@@ -202,6 +204,7 @@ runtime-benchmarks = [
|
||||
"pallet-membership/runtime-benchmarks",
|
||||
"pallet-mmr/runtime-benchmarks",
|
||||
"pallet-multisig/runtime-benchmarks",
|
||||
"pallet-preimage/runtime-benchmarks",
|
||||
"pallet-proxy/runtime-benchmarks",
|
||||
"pallet-scheduler/runtime-benchmarks",
|
||||
"pallet-society/runtime-benchmarks",
|
||||
@@ -243,6 +246,7 @@ try-runtime = [
|
||||
"pallet-identity/try-runtime",
|
||||
"pallet-scheduler/try-runtime",
|
||||
"pallet-offences/try-runtime",
|
||||
"pallet-preimage/try-runtime",
|
||||
"pallet-proxy/try-runtime",
|
||||
"pallet-randomness-collective-flip/try-runtime",
|
||||
"pallet-session/try-runtime",
|
||||
|
||||
@@ -337,6 +337,8 @@ parameter_types! {
|
||||
pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) *
|
||||
RuntimeBlockWeights::get().max_block;
|
||||
pub const MaxScheduledPerBlock: u32 = 50;
|
||||
// Retry a scheduled item every 10 blocks (1 minute) until the preimage exists.
|
||||
pub const NoPreimagePostponement: Option<u32> = Some(10);
|
||||
}
|
||||
|
||||
impl pallet_scheduler::Config for Runtime {
|
||||
@@ -349,6 +351,25 @@ impl pallet_scheduler::Config for Runtime {
|
||||
type MaxScheduledPerBlock = MaxScheduledPerBlock;
|
||||
type WeightInfo = pallet_scheduler::weights::SubstrateWeight<Runtime>;
|
||||
type OriginPrivilegeCmp = EqualPrivilegeOnly;
|
||||
type PreimageProvider = Preimage;
|
||||
type NoPreimagePostponement = NoPreimagePostponement;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const PreimageMaxSize: u32 = 4096 * 1024;
|
||||
pub const PreimageBaseDeposit: Balance = 1 * DOLLARS;
|
||||
// One cent: $10,000 / MB
|
||||
pub const PreimageByteDeposit: Balance = 1 * CENTS;
|
||||
}
|
||||
|
||||
impl pallet_preimage::Config for Runtime {
|
||||
type WeightInfo = pallet_preimage::weights::SubstrateWeight<Runtime>;
|
||||
type Event = Event;
|
||||
type Currency = Balances;
|
||||
type ManagerOrigin = EnsureRoot<AccountId>;
|
||||
type MaxSize = PreimageMaxSize;
|
||||
type BaseDeposit = PreimageBaseDeposit;
|
||||
type ByteDeposit = PreimageByteDeposit;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
@@ -688,8 +709,6 @@ parameter_types! {
|
||||
pub const MinimumDeposit: Balance = 100 * DOLLARS;
|
||||
pub const EnactmentPeriod: BlockNumber = 30 * 24 * 60 * MINUTES;
|
||||
pub const CooloffPeriod: BlockNumber = 28 * 24 * 60 * MINUTES;
|
||||
// One cent: $10,000 / MB
|
||||
pub const PreimageByteDeposit: Balance = 1 * CENTS;
|
||||
pub const MaxVotes: u32 = 100;
|
||||
pub const MaxProposals: u32 = 100;
|
||||
}
|
||||
@@ -1307,6 +1326,7 @@ construct_runtime!(
|
||||
Recovery: pallet_recovery,
|
||||
Vesting: pallet_vesting,
|
||||
Scheduler: pallet_scheduler,
|
||||
Preimage: pallet_preimage,
|
||||
Proxy: pallet_proxy,
|
||||
Multisig: pallet_multisig,
|
||||
Bounties: pallet_bounties,
|
||||
@@ -1686,6 +1706,7 @@ impl_runtime_apis! {
|
||||
list_benchmark!(list, extra, pallet_mmr, Mmr);
|
||||
list_benchmark!(list, extra, pallet_multisig, Multisig);
|
||||
list_benchmark!(list, extra, pallet_offences, OffencesBench::<Runtime>);
|
||||
list_benchmark!(list, extra, pallet_preimage, Preimage);
|
||||
list_benchmark!(list, extra, pallet_proxy, Proxy);
|
||||
list_benchmark!(list, extra, pallet_scheduler, Scheduler);
|
||||
list_benchmark!(list, extra, pallet_session, SessionBench::<Runtime>);
|
||||
@@ -1764,6 +1785,7 @@ impl_runtime_apis! {
|
||||
add_benchmark!(params, batches, pallet_mmr, Mmr);
|
||||
add_benchmark!(params, batches, pallet_multisig, Multisig);
|
||||
add_benchmark!(params, batches, pallet_offences, OffencesBench::<Runtime>);
|
||||
add_benchmark!(params, batches, pallet_preimage, Preimage);
|
||||
add_benchmark!(params, batches, pallet_proxy, Proxy);
|
||||
add_benchmark!(params, batches, pallet_scheduler, Scheduler);
|
||||
add_benchmark!(params, batches, pallet_session, SessionBench::<Runtime>);
|
||||
@@ -1811,11 +1833,13 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn call_size() {
|
||||
let size = core::mem::size_of::<Call>();
|
||||
assert!(
|
||||
core::mem::size_of::<Call>() <= 200,
|
||||
"size of Call is more than 200 bytes: some calls have too big arguments, use Box to reduce the
|
||||
size <= 200,
|
||||
"size of Call {} is more than 200 bytes: some calls have too big arguments, use Box to reduce the
|
||||
size of Call.
|
||||
If the limit is too strong, maybe consider increase the limit to 300.",
|
||||
size,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,6 +120,8 @@ impl pallet_scheduler::Config for Test {
|
||||
type MaxScheduledPerBlock = ();
|
||||
type WeightInfo = ();
|
||||
type OriginPrivilegeCmp = EqualPrivilegeOnly;
|
||||
type PreimageProvider = ();
|
||||
type NoPreimagePostponement = ();
|
||||
}
|
||||
parameter_types! {
|
||||
pub const ExistentialDeposit: u64 = 1;
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
[package]
|
||||
name = "pallet-preimage"
|
||||
version = "4.0.0-dev"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2021"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://substrate.io"
|
||||
repository = "https://github.com/paritytech/substrate/"
|
||||
description = "FRAME pallet for storing preimages of hashes"
|
||||
readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = ["derive"] }
|
||||
scale-info = { version = "1.0", default-features = false, features = ["derive"] }
|
||||
|
||||
sp-std = { version = "4.0.0", default-features = false, path = "../../primitives/std" }
|
||||
sp-io = { version = "4.0.0-dev", default-features = false, path = "../../primitives/io" }
|
||||
sp-core = { version = "4.1.0-dev", default-features = false, optional = true, path = "../../primitives/core" }
|
||||
sp-runtime = { version = "4.0.0-dev", default-features = false, path = "../../primitives/runtime" }
|
||||
frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" }
|
||||
frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" }
|
||||
frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../benchmarking", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
sp-core = { version = "4.1.0-dev", path = "../../primitives/core", default-features = false }
|
||||
pallet-balances = { version = "4.0.0-dev", path = "../balances" }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
runtime-benchmarks = [
|
||||
"frame-benchmarking",
|
||||
"frame-support/runtime-benchmarks",
|
||||
"frame-system/runtime-benchmarks",
|
||||
]
|
||||
std = [
|
||||
"codec/std",
|
||||
"scale-info/std",
|
||||
"sp-std/std",
|
||||
"sp-io/std",
|
||||
"sp-core/std",
|
||||
"sp-runtime/std",
|
||||
"frame-system/std",
|
||||
"frame-support/std",
|
||||
"frame-benchmarking/std",
|
||||
]
|
||||
try-runtime = ["frame-support/try-runtime"]
|
||||
@@ -0,0 +1,161 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2020-2021 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.
|
||||
|
||||
//! Preimage pallet benchmarking.
|
||||
|
||||
use super::*;
|
||||
use frame_benchmarking::{account, benchmarks, whitelist_account};
|
||||
use frame_support::assert_ok;
|
||||
use frame_system::RawOrigin;
|
||||
use sp_runtime::traits::Bounded;
|
||||
use sp_std::{prelude::*, vec};
|
||||
|
||||
use crate::Pallet as Preimage;
|
||||
|
||||
const SEED: u32 = 0;
|
||||
|
||||
fn funded_account<T: Config>(name: &'static str, index: u32) -> T::AccountId {
|
||||
let caller: T::AccountId = account(name, index, SEED);
|
||||
T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
|
||||
caller
|
||||
}
|
||||
|
||||
fn preimage_and_hash<T: Config>() -> (Vec<u8>, T::Hash) {
|
||||
sized_preimage_and_hash::<T>(T::MaxSize::get())
|
||||
}
|
||||
|
||||
fn sized_preimage_and_hash<T: Config>(size: u32) -> (Vec<u8>, T::Hash) {
|
||||
let mut preimage = vec![];
|
||||
preimage.resize(size as usize, 0);
|
||||
let hash = <T as frame_system::Config>::Hashing::hash(&preimage[..]);
|
||||
(preimage, hash)
|
||||
}
|
||||
|
||||
benchmarks! {
|
||||
// Expensive note - will reserve.
|
||||
note_preimage {
|
||||
let s in 0 .. T::MaxSize::get();
|
||||
let caller = funded_account::<T>("caller", 0);
|
||||
whitelist_account!(caller);
|
||||
let (preimage, hash) = sized_preimage_and_hash::<T>(s);
|
||||
}: _(RawOrigin::Signed(caller), preimage)
|
||||
verify {
|
||||
assert!(Preimage::<T>::have_preimage(&hash));
|
||||
}
|
||||
// Cheap note - will not reserve since it was requested.
|
||||
note_requested_preimage {
|
||||
let s in 0 .. T::MaxSize::get();
|
||||
let caller = funded_account::<T>("caller", 0);
|
||||
whitelist_account!(caller);
|
||||
let (preimage, hash) = sized_preimage_and_hash::<T>(s);
|
||||
assert_ok!(Preimage::<T>::request_preimage(T::ManagerOrigin::successful_origin(), hash.clone()));
|
||||
}: note_preimage(RawOrigin::Signed(caller), preimage)
|
||||
verify {
|
||||
assert!(Preimage::<T>::have_preimage(&hash));
|
||||
}
|
||||
// Cheap note - will not reserve since it's the manager.
|
||||
note_no_deposit_preimage {
|
||||
let s in 0 .. T::MaxSize::get();
|
||||
let (preimage, hash) = sized_preimage_and_hash::<T>(s);
|
||||
assert_ok!(Preimage::<T>::request_preimage(T::ManagerOrigin::successful_origin(), hash.clone()));
|
||||
}: note_preimage<T::Origin>(T::ManagerOrigin::successful_origin(), preimage)
|
||||
verify {
|
||||
assert!(Preimage::<T>::have_preimage(&hash));
|
||||
}
|
||||
|
||||
// Expensive unnote - will unreserve.
|
||||
unnote_preimage {
|
||||
let caller = funded_account::<T>("caller", 0);
|
||||
whitelist_account!(caller);
|
||||
let (preimage, hash) = preimage_and_hash::<T>();
|
||||
assert_ok!(Preimage::<T>::note_preimage(RawOrigin::Signed(caller.clone()).into(), preimage));
|
||||
}: _(RawOrigin::Signed(caller), hash.clone())
|
||||
verify {
|
||||
assert!(!Preimage::<T>::have_preimage(&hash));
|
||||
}
|
||||
// Cheap unnote - will not unreserve since there's no deposit held.
|
||||
unnote_no_deposit_preimage {
|
||||
let (preimage, hash) = preimage_and_hash::<T>();
|
||||
assert_ok!(Preimage::<T>::note_preimage(T::ManagerOrigin::successful_origin(), preimage));
|
||||
}: unnote_preimage<T::Origin>(T::ManagerOrigin::successful_origin(), hash.clone())
|
||||
verify {
|
||||
assert!(!Preimage::<T>::have_preimage(&hash));
|
||||
}
|
||||
|
||||
// Expensive request - will unreserve the noter's deposit.
|
||||
request_preimage {
|
||||
let (preimage, hash) = preimage_and_hash::<T>();
|
||||
let noter = funded_account::<T>("noter", 0);
|
||||
whitelist_account!(noter);
|
||||
assert_ok!(Preimage::<T>::note_preimage(RawOrigin::Signed(noter).into(), preimage));
|
||||
}: _<T::Origin>(T::ManagerOrigin::successful_origin(), hash)
|
||||
verify {
|
||||
assert_eq!(StatusFor::<T>::get(&hash), Some(RequestStatus::Requested(1)));
|
||||
}
|
||||
// Cheap request - would unreserve the deposit but none was held.
|
||||
request_no_deposit_preimage {
|
||||
let (preimage, hash) = preimage_and_hash::<T>();
|
||||
assert_ok!(Preimage::<T>::note_preimage(T::ManagerOrigin::successful_origin(), preimage));
|
||||
}: request_preimage<T::Origin>(T::ManagerOrigin::successful_origin(), hash)
|
||||
verify {
|
||||
assert_eq!(StatusFor::<T>::get(&hash), Some(RequestStatus::Requested(1)));
|
||||
}
|
||||
// Cheap request - the preimage is not yet noted, so deposit to unreserve.
|
||||
request_unnoted_preimage {
|
||||
let (_, hash) = preimage_and_hash::<T>();
|
||||
}: request_preimage<T::Origin>(T::ManagerOrigin::successful_origin(), hash)
|
||||
verify {
|
||||
assert_eq!(StatusFor::<T>::get(&hash), Some(RequestStatus::Requested(1)));
|
||||
}
|
||||
// Cheap request - the preimage is already requested, so just a counter bump.
|
||||
request_requested_preimage {
|
||||
let (_, hash) = preimage_and_hash::<T>();
|
||||
assert_ok!(Preimage::<T>::request_preimage(T::ManagerOrigin::successful_origin(), hash.clone()));
|
||||
}: request_preimage<T::Origin>(T::ManagerOrigin::successful_origin(), hash)
|
||||
verify {
|
||||
assert_eq!(StatusFor::<T>::get(&hash), Some(RequestStatus::Requested(2)));
|
||||
}
|
||||
|
||||
// Expensive unrequest - last reference and it's noted, so will destroy the preimage.
|
||||
unrequest_preimage {
|
||||
let (preimage, hash) = preimage_and_hash::<T>();
|
||||
assert_ok!(Preimage::<T>::request_preimage(T::ManagerOrigin::successful_origin(), hash.clone()));
|
||||
assert_ok!(Preimage::<T>::note_preimage(T::ManagerOrigin::successful_origin(), preimage));
|
||||
}: _<T::Origin>(T::ManagerOrigin::successful_origin(), hash.clone())
|
||||
verify {
|
||||
assert_eq!(StatusFor::<T>::get(&hash), None);
|
||||
}
|
||||
// Cheap unrequest - last reference, but it's not noted.
|
||||
unrequest_unnoted_preimage {
|
||||
let (_, hash) = preimage_and_hash::<T>();
|
||||
assert_ok!(Preimage::<T>::request_preimage(T::ManagerOrigin::successful_origin(), hash.clone()));
|
||||
}: unrequest_preimage<T::Origin>(T::ManagerOrigin::successful_origin(), hash.clone())
|
||||
verify {
|
||||
assert_eq!(StatusFor::<T>::get(&hash), None);
|
||||
}
|
||||
// Cheap unrequest - not the last reference.
|
||||
unrequest_multi_referenced_preimage {
|
||||
let (_, hash) = preimage_and_hash::<T>();
|
||||
assert_ok!(Preimage::<T>::request_preimage(T::ManagerOrigin::successful_origin(), hash.clone()));
|
||||
assert_ok!(Preimage::<T>::request_preimage(T::ManagerOrigin::successful_origin(), hash.clone()));
|
||||
}: unrequest_preimage<T::Origin>(T::ManagerOrigin::successful_origin(), hash.clone())
|
||||
verify {
|
||||
assert_eq!(StatusFor::<T>::get(&hash), Some(RequestStatus::Requested(1)));
|
||||
}
|
||||
|
||||
impl_benchmark_test_suite!(Preimage, crate::mock::new_test_ext(), crate::mock::Test);
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2017-2021 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.
|
||||
|
||||
//! # Preimage Pallet
|
||||
//!
|
||||
//! - [`Config`]
|
||||
//! - [`Call`]
|
||||
//!
|
||||
//! ## Overview
|
||||
//!
|
||||
//! The Preimage pallet allows for the users and the runtime to store the preimage
|
||||
//! of a hash on chain. This can be used by other pallets where storing and managing
|
||||
//! large byte-blobs.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
mod benchmarking;
|
||||
#[cfg(test)]
|
||||
mod mock;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
pub mod weights;
|
||||
|
||||
use sp_runtime::traits::{BadOrigin, Hash, Saturating};
|
||||
use sp_std::{convert::TryFrom, prelude::*};
|
||||
|
||||
use codec::{Decode, Encode, MaxEncodedLen};
|
||||
use frame_support::{
|
||||
ensure,
|
||||
pallet_prelude::Get,
|
||||
traits::{Currency, PreimageProvider, PreimageRecipient, ReservableCurrency},
|
||||
weights::Pays,
|
||||
BoundedVec,
|
||||
};
|
||||
use scale_info::TypeInfo;
|
||||
use weights::WeightInfo;
|
||||
|
||||
use frame_support::pallet_prelude::*;
|
||||
use frame_system::pallet_prelude::*;
|
||||
|
||||
pub use pallet::*;
|
||||
|
||||
/// A type to note whether a preimage is owned by a user or the system.
|
||||
#[derive(Clone, Eq, PartialEq, Encode, Decode, TypeInfo, MaxEncodedLen, RuntimeDebug)]
|
||||
pub enum RequestStatus<AccountId, Balance> {
|
||||
/// The associated preimage has not yet been requested by the system. The given deposit (if
|
||||
/// some) is being held until either it becomes requested or the user retracts the primage.
|
||||
Unrequested(Option<(AccountId, Balance)>),
|
||||
/// There are a non-zero number of outstanding requests for this hash by this chain. If there
|
||||
/// is a preimage registered, then it may be removed iff this counter becomes zero.
|
||||
Requested(u32),
|
||||
}
|
||||
|
||||
type BalanceOf<T> =
|
||||
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
|
||||
|
||||
#[frame_support::pallet]
|
||||
pub mod pallet {
|
||||
use super::*;
|
||||
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config {
|
||||
/// The overarching event type.
|
||||
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
|
||||
|
||||
/// The Weight information for this pallet.
|
||||
type WeightInfo: weights::WeightInfo;
|
||||
|
||||
/// Currency type for this pallet.
|
||||
type Currency: ReservableCurrency<Self::AccountId>;
|
||||
|
||||
/// An origin that can request a preimage be placed on-chain without a deposit or fee, or
|
||||
/// manage existing preimages.
|
||||
type ManagerOrigin: EnsureOrigin<Self::Origin>;
|
||||
|
||||
/// Max size allowed for a preimage.
|
||||
type MaxSize: Get<u32>;
|
||||
|
||||
/// The base deposit for placing a preimage on chain.
|
||||
type BaseDeposit: Get<BalanceOf<Self>>;
|
||||
|
||||
/// The per-byte deposit for placing a preimage on chain.
|
||||
type ByteDeposit: Get<BalanceOf<Self>>;
|
||||
}
|
||||
|
||||
#[pallet::pallet]
|
||||
#[pallet::generate_store(pub(super) trait Store)]
|
||||
#[pallet::generate_storage_info]
|
||||
pub struct Pallet<T>(PhantomData<T>);
|
||||
|
||||
#[pallet::event]
|
||||
#[pallet::generate_deposit(pub(super) fn deposit_event)]
|
||||
pub enum Event<T: Config> {
|
||||
/// A preimage has been noted.
|
||||
Noted { hash: T::Hash },
|
||||
/// A preimage has been requested.
|
||||
Requested { hash: T::Hash },
|
||||
/// A preimage has ben cleared.
|
||||
Cleared { hash: T::Hash },
|
||||
}
|
||||
|
||||
#[pallet::error]
|
||||
pub enum Error<T> {
|
||||
/// Preimage is too large to store on-chain.
|
||||
TooLarge,
|
||||
/// Preimage has already been noted on-chain.
|
||||
AlreadyNoted,
|
||||
/// The user is not authorized to perform this action.
|
||||
NotAuthorized,
|
||||
/// The preimage cannot be removed since it has not yet been noted.
|
||||
NotNoted,
|
||||
/// A preimage may not be removed when there are outstanding requests.
|
||||
Requested,
|
||||
/// The preimage request cannot be removed since no outstanding requests exist.
|
||||
NotRequested,
|
||||
}
|
||||
|
||||
/// The request status of a given hash.
|
||||
#[pallet::storage]
|
||||
pub(super) type StatusFor<T: Config> =
|
||||
StorageMap<_, Identity, T::Hash, RequestStatus<T::AccountId, BalanceOf<T>>>;
|
||||
|
||||
/// The preimages stored by this pallet.
|
||||
#[pallet::storage]
|
||||
pub(super) type PreimageFor<T: Config> =
|
||||
StorageMap<_, Identity, T::Hash, BoundedVec<u8, T::MaxSize>>;
|
||||
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {
|
||||
/// Register a preimage on-chain.
|
||||
///
|
||||
/// If the preimage was previously requested, no fees or deposits are taken for providing
|
||||
/// the preimage. Otherwise, a deposit is taken proportional to the size of the preimage.
|
||||
#[pallet::weight(T::WeightInfo::note_preimage(bytes.len() as u32))]
|
||||
pub fn note_preimage(origin: OriginFor<T>, bytes: Vec<u8>) -> DispatchResultWithPostInfo {
|
||||
// We accept a signed origin which will pay a deposit, or a root origin where a deposit
|
||||
// is not taken.
|
||||
let maybe_sender = Self::ensure_signed_or_manager(origin)?;
|
||||
let bounded_vec =
|
||||
BoundedVec::<u8, T::MaxSize>::try_from(bytes).map_err(|()| Error::<T>::TooLarge)?;
|
||||
let system_requested = Self::note_bytes(bounded_vec, maybe_sender.as_ref())?;
|
||||
if system_requested || maybe_sender.is_none() {
|
||||
Ok(Pays::No.into())
|
||||
} else {
|
||||
Ok(().into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear an unrequested preimage from the runtime storage.
|
||||
#[pallet::weight(T::WeightInfo::unnote_preimage())]
|
||||
pub fn unnote_preimage(origin: OriginFor<T>, hash: T::Hash) -> DispatchResult {
|
||||
let maybe_sender = Self::ensure_signed_or_manager(origin)?;
|
||||
Self::do_unnote_preimage(&hash, maybe_sender)
|
||||
}
|
||||
|
||||
/// Request a preimage be uploaded to the chain without paying any fees or deposits.
|
||||
///
|
||||
/// If the preimage requests has already been provided on-chain, we unreserve any deposit
|
||||
/// a user may have paid, and take the control of the preimage out of their hands.
|
||||
#[pallet::weight(T::WeightInfo::request_preimage())]
|
||||
pub fn request_preimage(origin: OriginFor<T>, hash: T::Hash) -> DispatchResult {
|
||||
T::ManagerOrigin::ensure_origin(origin)?;
|
||||
Self::do_request_preimage(&hash);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Clear a previously made request for a preimage.
|
||||
///
|
||||
/// NOTE: THIS MUST NOT BE CALLED ON `hash` MORE TIMES THAN `request_preimage`.
|
||||
#[pallet::weight(T::WeightInfo::unrequest_preimage())]
|
||||
pub fn unrequest_preimage(origin: OriginFor<T>, hash: T::Hash) -> DispatchResult {
|
||||
T::ManagerOrigin::ensure_origin(origin)?;
|
||||
Self::do_unrequest_preimage(&hash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> Pallet<T> {
|
||||
/// Ensure that the origin is either the `ManagerOrigin` or a signed origin.
|
||||
fn ensure_signed_or_manager(origin: T::Origin) -> Result<Option<T::AccountId>, BadOrigin> {
|
||||
if T::ManagerOrigin::ensure_origin(origin.clone()).is_ok() {
|
||||
return Ok(None)
|
||||
}
|
||||
let who = ensure_signed(origin)?;
|
||||
Ok(Some(who))
|
||||
}
|
||||
|
||||
/// Store some preimage on chain.
|
||||
///
|
||||
/// We verify that the preimage is within the bounds of what the pallet supports.
|
||||
///
|
||||
/// If the preimage was requested to be uploaded, then the user pays no deposits or tx fees.
|
||||
fn note_bytes(
|
||||
preimage: BoundedVec<u8, T::MaxSize>,
|
||||
maybe_depositor: Option<&T::AccountId>,
|
||||
) -> Result<bool, DispatchError> {
|
||||
let hash = T::Hashing::hash(&preimage);
|
||||
ensure!(!PreimageFor::<T>::contains_key(hash), Error::<T>::AlreadyNoted);
|
||||
|
||||
// We take a deposit only if there is a provided depositor, and the preimage was not
|
||||
// previously requested. This also allows the tx to pay no fee.
|
||||
let was_requested = match (StatusFor::<T>::get(hash), maybe_depositor) {
|
||||
(Some(RequestStatus::Requested(..)), _) => true,
|
||||
(Some(RequestStatus::Unrequested(..)), _) => Err(Error::<T>::AlreadyNoted)?,
|
||||
(None, None) => {
|
||||
StatusFor::<T>::insert(hash, RequestStatus::Unrequested(None));
|
||||
false
|
||||
},
|
||||
(None, Some(depositor)) => {
|
||||
let length = preimage.len() as u32;
|
||||
let deposit = T::BaseDeposit::get()
|
||||
.saturating_add(T::ByteDeposit::get().saturating_mul(length.into()));
|
||||
T::Currency::reserve(depositor, deposit)?;
|
||||
let status = RequestStatus::Unrequested(Some((depositor.clone(), deposit)));
|
||||
StatusFor::<T>::insert(hash, status);
|
||||
false
|
||||
},
|
||||
};
|
||||
|
||||
PreimageFor::<T>::insert(hash, preimage);
|
||||
Self::deposit_event(Event::Noted { hash });
|
||||
|
||||
Ok(was_requested)
|
||||
}
|
||||
|
||||
// This function will add a hash to the list of requested preimages.
|
||||
//
|
||||
// If the preimage already exists before the request is made, the deposit for the preimage is
|
||||
// returned to the user, and removed from their management.
|
||||
fn do_request_preimage(hash: &T::Hash) {
|
||||
let count = StatusFor::<T>::get(hash).map_or(1, |x| match x {
|
||||
RequestStatus::Requested(mut count) => {
|
||||
count.saturating_inc();
|
||||
count
|
||||
},
|
||||
RequestStatus::Unrequested(None) => 1,
|
||||
RequestStatus::Unrequested(Some((owner, deposit))) => {
|
||||
// Return the deposit - the preimage now has outstanding requests.
|
||||
T::Currency::unreserve(&owner, deposit);
|
||||
1
|
||||
},
|
||||
});
|
||||
StatusFor::<T>::insert(hash, RequestStatus::Requested(count));
|
||||
if count == 1 {
|
||||
Self::deposit_event(Event::Requested { hash: hash.clone() });
|
||||
}
|
||||
}
|
||||
|
||||
// Clear a preimage from the storage of the chain, returning any deposit that may be reserved.
|
||||
//
|
||||
// If `maybe_owner` is provided, we verify that it is the correct owner before clearing the
|
||||
// data.
|
||||
fn do_unnote_preimage(
|
||||
hash: &T::Hash,
|
||||
maybe_check_owner: Option<T::AccountId>,
|
||||
) -> DispatchResult {
|
||||
match StatusFor::<T>::get(hash).ok_or(Error::<T>::NotNoted)? {
|
||||
RequestStatus::Unrequested(Some((owner, deposit))) => {
|
||||
ensure!(
|
||||
maybe_check_owner.map_or(true, |c| &c == &owner),
|
||||
Error::<T>::NotAuthorized
|
||||
);
|
||||
T::Currency::unreserve(&owner, deposit);
|
||||
},
|
||||
RequestStatus::Unrequested(None) => {
|
||||
ensure!(maybe_check_owner.is_none(), Error::<T>::NotAuthorized);
|
||||
},
|
||||
RequestStatus::Requested(_) => Err(Error::<T>::Requested)?,
|
||||
}
|
||||
StatusFor::<T>::remove(hash);
|
||||
PreimageFor::<T>::remove(hash);
|
||||
Self::deposit_event(Event::Cleared { hash: hash.clone() });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Clear a preimage request.
|
||||
fn do_unrequest_preimage(hash: &T::Hash) -> DispatchResult {
|
||||
match StatusFor::<T>::get(hash).ok_or(Error::<T>::NotRequested)? {
|
||||
RequestStatus::Requested(mut count) if count > 1 => {
|
||||
count.saturating_dec();
|
||||
StatusFor::<T>::insert(hash, RequestStatus::Requested(count));
|
||||
},
|
||||
RequestStatus::Requested(count) => {
|
||||
debug_assert!(count == 1, "preimage request counter at zero?");
|
||||
PreimageFor::<T>::remove(hash);
|
||||
StatusFor::<T>::remove(hash);
|
||||
Self::deposit_event(Event::Cleared { hash: hash.clone() });
|
||||
},
|
||||
RequestStatus::Unrequested(_) => Err(Error::<T>::NotRequested)?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> PreimageProvider<T::Hash> for Pallet<T> {
|
||||
fn have_preimage(hash: &T::Hash) -> bool {
|
||||
PreimageFor::<T>::contains_key(hash)
|
||||
}
|
||||
|
||||
fn preimage_requested(hash: &T::Hash) -> bool {
|
||||
matches!(StatusFor::<T>::get(hash), Some(RequestStatus::Requested(..)))
|
||||
}
|
||||
|
||||
fn get_preimage(hash: &T::Hash) -> Option<Vec<u8>> {
|
||||
PreimageFor::<T>::get(hash).map(|preimage| preimage.to_vec())
|
||||
}
|
||||
|
||||
fn request_preimage(hash: &T::Hash) {
|
||||
Self::do_request_preimage(hash)
|
||||
}
|
||||
|
||||
fn unrequest_preimage(hash: &T::Hash) {
|
||||
let res = Self::do_unrequest_preimage(hash);
|
||||
debug_assert!(res.is_ok(), "do_unrequest_preimage failed - counter underflow?");
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> PreimageRecipient<T::Hash> for Pallet<T> {
|
||||
type MaxSize = T::MaxSize;
|
||||
|
||||
fn note_preimage(bytes: BoundedVec<u8, Self::MaxSize>) {
|
||||
// We don't really care if this fails, since that's only the case if someone else has
|
||||
// already noted it.
|
||||
let _ = Self::note_bytes(bytes, None);
|
||||
}
|
||||
|
||||
fn unnote_preimage(hash: &T::Hash) {
|
||||
// Should never fail if authorization check is skipped.
|
||||
let res = Self::do_unnote_preimage(hash, None);
|
||||
debug_assert!(res.is_ok(), "unnote_preimage failed - request outstanding?");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2017-2021 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.
|
||||
|
||||
//! # Scheduler test environment.
|
||||
|
||||
use super::*;
|
||||
|
||||
use crate as pallet_preimage;
|
||||
use frame_support::{
|
||||
ord_parameter_types, parameter_types, traits::Everything, weights::constants::RocksDbWeight,
|
||||
};
|
||||
use frame_system::EnsureSignedBy;
|
||||
use sp_core::H256;
|
||||
use sp_runtime::{
|
||||
testing::Header,
|
||||
traits::{BlakeTwo256, IdentityLookup},
|
||||
Perbill,
|
||||
};
|
||||
|
||||
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,
|
||||
Balances: pallet_balances,
|
||||
Preimage: pallet_preimage,
|
||||
}
|
||||
);
|
||||
|
||||
parameter_types! {
|
||||
pub const BlockHashCount: u64 = 250;
|
||||
pub BlockWeights: frame_system::limits::BlockWeights =
|
||||
frame_system::limits::BlockWeights::simple_max(2_000_000_000_000);
|
||||
}
|
||||
impl frame_system::Config for Test {
|
||||
type BaseCallFilter = Everything;
|
||||
type BlockWeights = ();
|
||||
type BlockLength = ();
|
||||
type DbWeight = RocksDbWeight;
|
||||
type Origin = Origin;
|
||||
type Call = Call;
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = H256;
|
||||
type Hashing = BlakeTwo256;
|
||||
type AccountId = u64;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Header = Header;
|
||||
type Event = Event;
|
||||
type BlockHashCount = BlockHashCount;
|
||||
type Version = ();
|
||||
type PalletInfo = PalletInfo;
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
type SystemWeightInfo = ();
|
||||
type SS58Prefix = ();
|
||||
type OnSetCode = ();
|
||||
type MaxConsumers = frame_support::traits::ConstU32<16>;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const ExistentialDeposit: u64 = 5;
|
||||
pub const MaxReserves: u32 = 50;
|
||||
}
|
||||
|
||||
impl pallet_balances::Config for Test {
|
||||
type Balance = u64;
|
||||
type Event = Event;
|
||||
type DustRemoval = ();
|
||||
type ExistentialDeposit = ExistentialDeposit;
|
||||
type AccountStore = System;
|
||||
type WeightInfo = ();
|
||||
type MaxLocks = ();
|
||||
type MaxReserves = MaxReserves;
|
||||
type ReserveIdentifier = [u8; 8];
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * BlockWeights::get().max_block;
|
||||
pub const MaxScheduledPerBlock: u32 = 10;
|
||||
pub const MaxSize: u32 = 1024;
|
||||
pub const BaseDeposit: u64 = 2;
|
||||
pub const ByteDeposit: u64 = 1;
|
||||
}
|
||||
|
||||
ord_parameter_types! {
|
||||
pub const One: u64 = 1;
|
||||
}
|
||||
|
||||
impl Config for Test {
|
||||
type WeightInfo = ();
|
||||
type Event = Event;
|
||||
type Currency = Balances;
|
||||
type ManagerOrigin = EnsureSignedBy<One, u64>;
|
||||
type MaxSize = MaxSize;
|
||||
type BaseDeposit = BaseDeposit;
|
||||
type ByteDeposit = ByteDeposit;
|
||||
}
|
||||
|
||||
pub fn new_test_ext() -> sp_io::TestExternalities {
|
||||
let mut t = frame_system::GenesisConfig::default().build_storage::<Test>().unwrap();
|
||||
let balances = pallet_balances::GenesisConfig::<Test> {
|
||||
balances: vec![(1, 100), (2, 100), (3, 100), (4, 100), (5, 100)],
|
||||
};
|
||||
balances.assimilate_storage(&mut t).unwrap();
|
||||
t.into()
|
||||
}
|
||||
|
||||
pub fn hashed(data: impl AsRef<[u8]>) -> H256 {
|
||||
BlakeTwo256::hash(data.as_ref())
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2017-2021 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.
|
||||
|
||||
//! # Scheduler tests.
|
||||
|
||||
use super::*;
|
||||
use crate::mock::*;
|
||||
|
||||
use frame_support::{assert_noop, assert_ok};
|
||||
use pallet_balances::Error as BalancesError;
|
||||
|
||||
#[test]
|
||||
fn user_note_preimage_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_ok!(Preimage::note_preimage(Origin::signed(2), vec![1]));
|
||||
assert_eq!(Balances::reserved_balance(2), 3);
|
||||
assert_eq!(Balances::free_balance(2), 97);
|
||||
|
||||
let h = hashed([1]);
|
||||
assert!(Preimage::have_preimage(&h));
|
||||
assert_eq!(Preimage::get_preimage(&h), Some(vec![1]));
|
||||
|
||||
assert_noop!(
|
||||
Preimage::note_preimage(Origin::signed(2), vec![1]),
|
||||
Error::<Test>::AlreadyNoted
|
||||
);
|
||||
assert_noop!(
|
||||
Preimage::note_preimage(Origin::signed(0), vec![2]),
|
||||
BalancesError::<Test>::InsufficientBalance
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn manager_note_preimage_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_ok!(Preimage::note_preimage(Origin::signed(1), vec![1]));
|
||||
assert_eq!(Balances::reserved_balance(1), 0);
|
||||
assert_eq!(Balances::free_balance(1), 100);
|
||||
|
||||
let h = hashed([1]);
|
||||
assert!(Preimage::have_preimage(&h));
|
||||
assert_eq!(Preimage::get_preimage(&h), Some(vec![1]));
|
||||
|
||||
assert_noop!(
|
||||
Preimage::note_preimage(Origin::signed(1), vec![1]),
|
||||
Error::<Test>::AlreadyNoted
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn user_unnote_preimage_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_ok!(Preimage::note_preimage(Origin::signed(2), vec![1]));
|
||||
assert_noop!(
|
||||
Preimage::unnote_preimage(Origin::signed(3), hashed([1])),
|
||||
Error::<Test>::NotAuthorized
|
||||
);
|
||||
assert_noop!(
|
||||
Preimage::unnote_preimage(Origin::signed(2), hashed([2])),
|
||||
Error::<Test>::NotNoted
|
||||
);
|
||||
|
||||
assert_ok!(Preimage::unnote_preimage(Origin::signed(2), hashed([1])));
|
||||
assert_noop!(
|
||||
Preimage::unnote_preimage(Origin::signed(2), hashed([1])),
|
||||
Error::<Test>::NotNoted
|
||||
);
|
||||
|
||||
let h = hashed([1]);
|
||||
assert!(!Preimage::have_preimage(&h));
|
||||
assert_eq!(Preimage::get_preimage(&h), None);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn manager_unnote_preimage_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_ok!(Preimage::note_preimage(Origin::signed(1), vec![1]));
|
||||
assert_ok!(Preimage::unnote_preimage(Origin::signed(1), hashed([1])));
|
||||
assert_noop!(
|
||||
Preimage::unnote_preimage(Origin::signed(1), hashed([1])),
|
||||
Error::<Test>::NotNoted
|
||||
);
|
||||
|
||||
let h = hashed([1]);
|
||||
assert!(!Preimage::have_preimage(&h));
|
||||
assert_eq!(Preimage::get_preimage(&h), None);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn manager_unnote_user_preimage_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_ok!(Preimage::note_preimage(Origin::signed(2), vec![1]));
|
||||
assert_noop!(
|
||||
Preimage::unnote_preimage(Origin::signed(3), hashed([1])),
|
||||
Error::<Test>::NotAuthorized
|
||||
);
|
||||
assert_noop!(
|
||||
Preimage::unnote_preimage(Origin::signed(2), hashed([2])),
|
||||
Error::<Test>::NotNoted
|
||||
);
|
||||
|
||||
assert_ok!(Preimage::unnote_preimage(Origin::signed(1), hashed([1])));
|
||||
|
||||
let h = hashed([1]);
|
||||
assert!(!Preimage::have_preimage(&h));
|
||||
assert_eq!(Preimage::get_preimage(&h), None);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn requested_then_noted_preimage_cannot_be_unnoted() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_ok!(Preimage::note_preimage(Origin::signed(1), vec![1]));
|
||||
assert_ok!(Preimage::request_preimage(Origin::signed(1), hashed([1])));
|
||||
assert_noop!(
|
||||
Preimage::unnote_preimage(Origin::signed(1), hashed([1])),
|
||||
Error::<Test>::Requested
|
||||
);
|
||||
|
||||
let h = hashed([1]);
|
||||
assert!(Preimage::have_preimage(&h));
|
||||
assert_eq!(Preimage::get_preimage(&h), Some(vec![1]));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn request_note_order_makes_no_difference() {
|
||||
let one_way = new_test_ext().execute_with(|| {
|
||||
assert_ok!(Preimage::request_preimage(Origin::signed(1), hashed([1])));
|
||||
assert_ok!(Preimage::note_preimage(Origin::signed(1), vec![1]));
|
||||
(
|
||||
StatusFor::<Test>::iter().collect::<Vec<_>>(),
|
||||
PreimageFor::<Test>::iter().collect::<Vec<_>>(),
|
||||
)
|
||||
});
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_ok!(Preimage::note_preimage(Origin::signed(1), vec![1]));
|
||||
assert_ok!(Preimage::request_preimage(Origin::signed(1), hashed([1])));
|
||||
let other_way = (
|
||||
StatusFor::<Test>::iter().collect::<Vec<_>>(),
|
||||
PreimageFor::<Test>::iter().collect::<Vec<_>>(),
|
||||
);
|
||||
assert_eq!(one_way, other_way);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn requested_then_user_noted_preimage_is_free() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_ok!(Preimage::request_preimage(Origin::signed(1), hashed([1])));
|
||||
assert_ok!(Preimage::note_preimage(Origin::signed(2), vec![1]));
|
||||
assert_eq!(Balances::reserved_balance(2), 0);
|
||||
assert_eq!(Balances::free_balance(2), 100);
|
||||
|
||||
let h = hashed([1]);
|
||||
assert!(Preimage::have_preimage(&h));
|
||||
assert_eq!(Preimage::get_preimage(&h), Some(vec![1]));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn request_user_note_order_makes_no_difference() {
|
||||
let one_way = new_test_ext().execute_with(|| {
|
||||
assert_ok!(Preimage::request_preimage(Origin::signed(1), hashed([1])));
|
||||
assert_ok!(Preimage::note_preimage(Origin::signed(2), vec![1]));
|
||||
(
|
||||
StatusFor::<Test>::iter().collect::<Vec<_>>(),
|
||||
PreimageFor::<Test>::iter().collect::<Vec<_>>(),
|
||||
)
|
||||
});
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_ok!(Preimage::note_preimage(Origin::signed(2), vec![1]));
|
||||
assert_ok!(Preimage::request_preimage(Origin::signed(1), hashed([1])));
|
||||
let other_way = (
|
||||
StatusFor::<Test>::iter().collect::<Vec<_>>(),
|
||||
PreimageFor::<Test>::iter().collect::<Vec<_>>(),
|
||||
);
|
||||
assert_eq!(one_way, other_way);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unrequest_preimage_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_ok!(Preimage::request_preimage(Origin::signed(1), hashed([1])));
|
||||
assert_ok!(Preimage::request_preimage(Origin::signed(1), hashed([1])));
|
||||
assert_ok!(Preimage::note_preimage(Origin::signed(2), vec![1]));
|
||||
assert_noop!(
|
||||
Preimage::unrequest_preimage(Origin::signed(1), hashed([2])),
|
||||
Error::<Test>::NotRequested
|
||||
);
|
||||
|
||||
assert_ok!(Preimage::unrequest_preimage(Origin::signed(1), hashed([1])));
|
||||
assert!(Preimage::have_preimage(&hashed([1])));
|
||||
|
||||
assert_ok!(Preimage::unrequest_preimage(Origin::signed(1), hashed([1])));
|
||||
assert_noop!(
|
||||
Preimage::unrequest_preimage(Origin::signed(1), hashed([1])),
|
||||
Error::<Test>::NotRequested
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn user_noted_then_requested_preimage_is_refunded_once_only() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_ok!(Preimage::note_preimage(Origin::signed(2), vec![1; 3]));
|
||||
assert_ok!(Preimage::note_preimage(Origin::signed(2), vec![1]));
|
||||
assert_ok!(Preimage::request_preimage(Origin::signed(1), hashed([1])));
|
||||
assert_ok!(Preimage::unrequest_preimage(Origin::signed(1), hashed([1])));
|
||||
// Still have reserve from `vec[1; 3]`.
|
||||
assert_eq!(Balances::reserved_balance(2), 5);
|
||||
assert_eq!(Balances::free_balance(2), 95);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2021 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_preimage
|
||||
//!
|
||||
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
|
||||
//! DATE: 2021-12-10, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
|
||||
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128
|
||||
|
||||
// Executed Command:
|
||||
// target/release/substrate
|
||||
// benchmark
|
||||
// --chain=dev
|
||||
// --steps=50
|
||||
// --repeat=20
|
||||
// --pallet=pallet_preimage
|
||||
// --extrinsic=*
|
||||
// --execution=wasm
|
||||
// --wasm-execution=compiled
|
||||
// --heap-pages=4096
|
||||
// --output=./frame/preimage/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_preimage.
|
||||
pub trait WeightInfo {
|
||||
fn note_preimage(s: u32, ) -> Weight;
|
||||
fn note_requested_preimage(s: u32, ) -> Weight;
|
||||
fn note_no_deposit_preimage(s: u32, ) -> Weight;
|
||||
fn unnote_preimage() -> Weight;
|
||||
fn unnote_no_deposit_preimage() -> Weight;
|
||||
fn request_preimage() -> Weight;
|
||||
fn request_no_deposit_preimage() -> Weight;
|
||||
fn request_unnoted_preimage() -> Weight;
|
||||
fn request_requested_preimage() -> Weight;
|
||||
fn unrequest_preimage() -> Weight;
|
||||
fn unrequest_unnoted_preimage() -> Weight;
|
||||
fn unrequest_multi_referenced_preimage() -> Weight;
|
||||
}
|
||||
|
||||
/// Weights for pallet_preimage using the Substrate node and recommended hardware.
|
||||
pub struct SubstrateWeight<T>(PhantomData<T>);
|
||||
impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
||||
// Storage: Preimage PreimageFor (r:1 w:1)
|
||||
// Storage: Preimage StatusFor (r:1 w:1)
|
||||
fn note_preimage(s: u32, ) -> Weight {
|
||||
(0 as Weight)
|
||||
// Standard Error: 0
|
||||
.saturating_add((2_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(2 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(2 as Weight))
|
||||
}
|
||||
// Storage: Preimage PreimageFor (r:1 w:1)
|
||||
// Storage: Preimage StatusFor (r:1 w:0)
|
||||
fn note_requested_preimage(s: u32, ) -> Weight {
|
||||
(0 as Weight)
|
||||
// Standard Error: 0
|
||||
.saturating_add((2_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(2 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(1 as Weight))
|
||||
}
|
||||
// Storage: Preimage PreimageFor (r:1 w:1)
|
||||
// Storage: Preimage StatusFor (r:1 w:0)
|
||||
fn note_no_deposit_preimage(s: u32, ) -> Weight {
|
||||
(0 as Weight)
|
||||
// Standard Error: 0
|
||||
.saturating_add((2_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(2 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(1 as Weight))
|
||||
}
|
||||
// Storage: Preimage StatusFor (r:1 w:1)
|
||||
// Storage: Preimage PreimageFor (r:0 w:1)
|
||||
fn unnote_preimage() -> Weight {
|
||||
(60_560_000 as Weight)
|
||||
.saturating_add(T::DbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(2 as Weight))
|
||||
}
|
||||
// Storage: Preimage StatusFor (r:1 w:1)
|
||||
// Storage: Preimage PreimageFor (r:0 w:1)
|
||||
fn unnote_no_deposit_preimage() -> Weight {
|
||||
(37_575_000 as Weight)
|
||||
.saturating_add(T::DbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(2 as Weight))
|
||||
}
|
||||
// Storage: Preimage StatusFor (r:1 w:1)
|
||||
fn request_preimage() -> Weight {
|
||||
(56_868_000 as Weight)
|
||||
.saturating_add(T::DbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(1 as Weight))
|
||||
}
|
||||
// Storage: Preimage StatusFor (r:1 w:1)
|
||||
fn request_no_deposit_preimage() -> Weight {
|
||||
(37_058_000 as Weight)
|
||||
.saturating_add(T::DbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(1 as Weight))
|
||||
}
|
||||
// Storage: Preimage StatusFor (r:1 w:1)
|
||||
fn request_unnoted_preimage() -> Weight {
|
||||
(21_500_000 as Weight)
|
||||
.saturating_add(T::DbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(1 as Weight))
|
||||
}
|
||||
// Storage: Preimage StatusFor (r:1 w:1)
|
||||
fn request_requested_preimage() -> Weight {
|
||||
(7_798_000 as Weight)
|
||||
.saturating_add(T::DbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(1 as Weight))
|
||||
}
|
||||
// Storage: Preimage StatusFor (r:1 w:1)
|
||||
// Storage: Preimage PreimageFor (r:0 w:1)
|
||||
fn unrequest_preimage() -> Weight {
|
||||
(37_771_000 as Weight)
|
||||
.saturating_add(T::DbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(2 as Weight))
|
||||
}
|
||||
// Storage: Preimage StatusFor (r:1 w:1)
|
||||
// Storage: Preimage PreimageFor (r:0 w:1)
|
||||
fn unrequest_unnoted_preimage() -> Weight {
|
||||
(22_913_000 as Weight)
|
||||
.saturating_add(T::DbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(2 as Weight))
|
||||
}
|
||||
// Storage: Preimage StatusFor (r:1 w:1)
|
||||
fn unrequest_multi_referenced_preimage() -> Weight {
|
||||
(7_608_000 as Weight)
|
||||
.saturating_add(T::DbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(1 as Weight))
|
||||
}
|
||||
}
|
||||
|
||||
// For backwards compatibility and tests
|
||||
impl WeightInfo for () {
|
||||
// Storage: Preimage PreimageFor (r:1 w:1)
|
||||
// Storage: Preimage StatusFor (r:1 w:1)
|
||||
fn note_preimage(s: u32, ) -> Weight {
|
||||
(0 as Weight)
|
||||
// Standard Error: 0
|
||||
.saturating_add((2_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads(2 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes(2 as Weight))
|
||||
}
|
||||
// Storage: Preimage PreimageFor (r:1 w:1)
|
||||
// Storage: Preimage StatusFor (r:1 w:0)
|
||||
fn note_requested_preimage(s: u32, ) -> Weight {
|
||||
(0 as Weight)
|
||||
// Standard Error: 0
|
||||
.saturating_add((2_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads(2 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
|
||||
}
|
||||
// Storage: Preimage PreimageFor (r:1 w:1)
|
||||
// Storage: Preimage StatusFor (r:1 w:0)
|
||||
fn note_no_deposit_preimage(s: u32, ) -> Weight {
|
||||
(0 as Weight)
|
||||
// Standard Error: 0
|
||||
.saturating_add((2_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads(2 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
|
||||
}
|
||||
// Storage: Preimage StatusFor (r:1 w:1)
|
||||
// Storage: Preimage PreimageFor (r:0 w:1)
|
||||
fn unnote_preimage() -> Weight {
|
||||
(60_560_000 as Weight)
|
||||
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes(2 as Weight))
|
||||
}
|
||||
// Storage: Preimage StatusFor (r:1 w:1)
|
||||
// Storage: Preimage PreimageFor (r:0 w:1)
|
||||
fn unnote_no_deposit_preimage() -> Weight {
|
||||
(37_575_000 as Weight)
|
||||
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes(2 as Weight))
|
||||
}
|
||||
// Storage: Preimage StatusFor (r:1 w:1)
|
||||
fn request_preimage() -> Weight {
|
||||
(56_868_000 as Weight)
|
||||
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
|
||||
}
|
||||
// Storage: Preimage StatusFor (r:1 w:1)
|
||||
fn request_no_deposit_preimage() -> Weight {
|
||||
(37_058_000 as Weight)
|
||||
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
|
||||
}
|
||||
// Storage: Preimage StatusFor (r:1 w:1)
|
||||
fn request_unnoted_preimage() -> Weight {
|
||||
(21_500_000 as Weight)
|
||||
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
|
||||
}
|
||||
// Storage: Preimage StatusFor (r:1 w:1)
|
||||
fn request_requested_preimage() -> Weight {
|
||||
(7_798_000 as Weight)
|
||||
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
|
||||
}
|
||||
// Storage: Preimage StatusFor (r:1 w:1)
|
||||
// Storage: Preimage PreimageFor (r:0 w:1)
|
||||
fn unrequest_preimage() -> Weight {
|
||||
(37_771_000 as Weight)
|
||||
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes(2 as Weight))
|
||||
}
|
||||
// Storage: Preimage StatusFor (r:1 w:1)
|
||||
// Storage: Preimage PreimageFor (r:0 w:1)
|
||||
fn unrequest_unnoted_preimage() -> Weight {
|
||||
(22_913_000 as Weight)
|
||||
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes(2 as Weight))
|
||||
}
|
||||
// Storage: Preimage StatusFor (r:1 w:1)
|
||||
fn unrequest_multi_referenced_preimage() -> Weight {
|
||||
(7_608_000 as Weight)
|
||||
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
|
||||
}
|
||||
}
|
||||
@@ -3,44 +3,45 @@ name = "pallet-scheduler"
|
||||
version = "4.0.0-dev"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2021"
|
||||
license = "Unlicense"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://substrate.io"
|
||||
repository = "https://github.com/paritytech/substrate/"
|
||||
description = "FRAME example pallet"
|
||||
description = "FRAME Scheduler pallet"
|
||||
readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false }
|
||||
codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = ["derive"] }
|
||||
scale-info = { version = "1.0", default-features = false, features = ["derive"] }
|
||||
frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" }
|
||||
frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" }
|
||||
sp-runtime = { version = "4.0.0-dev", default-features = false, path = "../../primitives/runtime" }
|
||||
sp-std = { version = "4.0.0", default-features = false, path = "../../primitives/std" }
|
||||
sp-io = { version = "4.0.0-dev", default-features = false, path = "../../primitives/io" }
|
||||
log = { version = "0.4.14", default-features = false }
|
||||
sp-std = { version = "4.0.0-dev", default-features = false, path = "../../primitives/std" }
|
||||
sp-io = { version = "4.0.0-dev", default-features = false, path = "../../primitives/io" }
|
||||
|
||||
sp-runtime = { version = "4.0.0-dev", default-features = false, path = "../../primitives/runtime" }
|
||||
frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" }
|
||||
frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" }
|
||||
frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../benchmarking", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
sp-core = { version = "4.1.0-dev", path = "../../primitives/core", default-features = false }
|
||||
substrate-test-utils = { version = "4.0.0-dev", path = "../../test-utils" }
|
||||
pallet-preimage = { version = "4.0.0-dev", path = "../preimage" }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"codec/std",
|
||||
"scale-info/std",
|
||||
"sp-runtime/std",
|
||||
"frame-benchmarking/std",
|
||||
"frame-support/std",
|
||||
"frame-system/std",
|
||||
"sp-io/std",
|
||||
"sp-std/std",
|
||||
"log/std",
|
||||
]
|
||||
runtime-benchmarks = [
|
||||
"frame-benchmarking/runtime-benchmarks",
|
||||
"frame-benchmarking",
|
||||
"frame-support/runtime-benchmarks",
|
||||
"frame-system/runtime-benchmarks",
|
||||
]
|
||||
std = [
|
||||
"codec/std",
|
||||
"scale-info/std",
|
||||
"log/std",
|
||||
"sp-std/std",
|
||||
"sp-io/std",
|
||||
"sp-runtime/std",
|
||||
"frame-system/std",
|
||||
"frame-support/std",
|
||||
"frame-benchmarking/std",
|
||||
]
|
||||
try-runtime = ["frame-support/try-runtime"]
|
||||
|
||||
@@ -17,12 +17,14 @@
|
||||
|
||||
//! Scheduler pallet benchmarking.
|
||||
|
||||
#![cfg(feature = "runtime-benchmarks")]
|
||||
|
||||
use super::*;
|
||||
use frame_benchmarking::benchmarks;
|
||||
use frame_support::{ensure, traits::OnInitialize};
|
||||
use frame_support::{
|
||||
ensure,
|
||||
traits::{OnInitialize, PreimageProvider, PreimageRecipient},
|
||||
};
|
||||
use frame_system::RawOrigin;
|
||||
use sp_runtime::traits::Hash;
|
||||
use sp_std::{prelude::*, vec};
|
||||
|
||||
use crate::Pallet as Scheduler;
|
||||
@@ -30,37 +32,184 @@ use frame_system::Pallet as System;
|
||||
|
||||
const BLOCK_NUMBER: u32 = 2;
|
||||
|
||||
// Add `n` named items to the schedule
|
||||
fn fill_schedule<T: Config>(when: T::BlockNumber, n: u32) -> Result<(), &'static str> {
|
||||
// Essentially a no-op call.
|
||||
let call = frame_system::Call::set_storage { items: vec![] };
|
||||
/// Add `n` named items to the schedule.
|
||||
///
|
||||
/// For `resolved`:
|
||||
/// - `None`: aborted (hash without preimage)
|
||||
/// - `Some(true)`: hash resolves into call if possible, plain call otherwise
|
||||
/// - `Some(false)`: plain call
|
||||
fn fill_schedule<T: Config>(
|
||||
when: T::BlockNumber,
|
||||
n: u32,
|
||||
periodic: bool,
|
||||
named: bool,
|
||||
resolved: Option<bool>,
|
||||
) -> Result<(), &'static str> {
|
||||
for i in 0..n {
|
||||
// Named schedule is strictly heavier than anonymous
|
||||
Scheduler::<T>::do_schedule_named(
|
||||
i.encode(),
|
||||
DispatchTime::At(when),
|
||||
// Add periodicity
|
||||
Some((T::BlockNumber::one(), 100)),
|
||||
// HARD_DEADLINE priority means it gets executed no matter what
|
||||
0,
|
||||
frame_system::RawOrigin::Root.into(),
|
||||
call.clone().into(),
|
||||
)?;
|
||||
let (call, hash) = call_and_hash::<T>(i);
|
||||
let call_or_hash = match resolved {
|
||||
Some(true) => {
|
||||
T::PreimageProvider::note_preimage(call.encode().try_into().unwrap());
|
||||
if T::PreimageProvider::have_preimage(&hash) {
|
||||
CallOrHashOf::<T>::Hash(hash)
|
||||
} else {
|
||||
call.into()
|
||||
}
|
||||
},
|
||||
Some(false) => call.into(),
|
||||
None => CallOrHashOf::<T>::Hash(hash),
|
||||
};
|
||||
let period = match periodic {
|
||||
true => Some(((i + 100).into(), 100)),
|
||||
false => None,
|
||||
};
|
||||
let t = DispatchTime::At(when);
|
||||
let origin = frame_system::RawOrigin::Root.into();
|
||||
if named {
|
||||
Scheduler::<T>::do_schedule_named(i.encode(), t, period, 0, origin, call_or_hash)?;
|
||||
} else {
|
||||
Scheduler::<T>::do_schedule(t, period, 0, origin, call_or_hash)?;
|
||||
}
|
||||
}
|
||||
ensure!(Agenda::<T>::get(when).len() == n as usize, "didn't fill schedule");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn call_and_hash<T: Config>(i: u32) -> (<T as Config>::Call, T::Hash) {
|
||||
// Essentially a no-op call.
|
||||
let call: <T as Config>::Call = frame_system::Call::remark { remark: i.encode() }.into();
|
||||
let hash = T::Hashing::hash_of(&call);
|
||||
(call, hash)
|
||||
}
|
||||
|
||||
benchmarks! {
|
||||
on_initialize_periodic_named_resolved {
|
||||
let s in 1 .. T::MaxScheduledPerBlock::get();
|
||||
let when = BLOCK_NUMBER.into();
|
||||
fill_schedule::<T>(when, s, true, true, Some(true))?;
|
||||
}: { Scheduler::<T>::on_initialize(BLOCK_NUMBER.into()); }
|
||||
verify {
|
||||
assert_eq!(System::<T>::event_count(), s * 2);
|
||||
for i in 0..s {
|
||||
assert_eq!(Agenda::<T>::get(when + (i + 100).into()).len(), 1 as usize);
|
||||
}
|
||||
}
|
||||
|
||||
on_initialize_named_resolved {
|
||||
let s in 1 .. T::MaxScheduledPerBlock::get();
|
||||
let when = BLOCK_NUMBER.into();
|
||||
fill_schedule::<T>(when, s, false, true, Some(true))?;
|
||||
}: { Scheduler::<T>::on_initialize(BLOCK_NUMBER.into()); }
|
||||
verify {
|
||||
assert_eq!(System::<T>::event_count(), s * 2);
|
||||
assert!(Agenda::<T>::iter().count() == 0);
|
||||
}
|
||||
|
||||
on_initialize_periodic_resolved {
|
||||
let s in 1 .. T::MaxScheduledPerBlock::get();
|
||||
let when = BLOCK_NUMBER.into();
|
||||
fill_schedule::<T>(when, s, true, false, Some(true))?;
|
||||
}: { Scheduler::<T>::on_initialize(BLOCK_NUMBER.into()); }
|
||||
verify {
|
||||
assert_eq!(System::<T>::event_count(), s * 2);
|
||||
for i in 0..s {
|
||||
assert_eq!(Agenda::<T>::get(when + (i + 100).into()).len(), 1 as usize);
|
||||
}
|
||||
}
|
||||
|
||||
on_initialize_resolved {
|
||||
let s in 1 .. T::MaxScheduledPerBlock::get();
|
||||
let when = BLOCK_NUMBER.into();
|
||||
fill_schedule::<T>(when, s, false, false, Some(true))?;
|
||||
}: { Scheduler::<T>::on_initialize(BLOCK_NUMBER.into()); }
|
||||
verify {
|
||||
assert_eq!(System::<T>::event_count(), s * 2);
|
||||
assert!(Agenda::<T>::iter().count() == 0);
|
||||
}
|
||||
|
||||
on_initialize_named_aborted {
|
||||
let s in 1 .. T::MaxScheduledPerBlock::get();
|
||||
let when = BLOCK_NUMBER.into();
|
||||
fill_schedule::<T>(when, s, false, true, None)?;
|
||||
}: { Scheduler::<T>::on_initialize(BLOCK_NUMBER.into()); }
|
||||
verify {
|
||||
assert_eq!(System::<T>::event_count(), 0);
|
||||
if let Some(delay) = T::NoPreimagePostponement::get() {
|
||||
assert_eq!(Agenda::<T>::get(when + delay).len(), s as usize);
|
||||
} else {
|
||||
assert!(Agenda::<T>::iter().count() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
on_initialize_aborted {
|
||||
let s in 1 .. T::MaxScheduledPerBlock::get();
|
||||
let when = BLOCK_NUMBER.into();
|
||||
fill_schedule::<T>(when, s, false, false, None)?;
|
||||
}: { Scheduler::<T>::on_initialize(BLOCK_NUMBER.into()); }
|
||||
verify {
|
||||
assert_eq!(System::<T>::event_count(), 0);
|
||||
if let Some(delay) = T::NoPreimagePostponement::get() {
|
||||
assert_eq!(Agenda::<T>::get(when + delay).len(), s as usize);
|
||||
} else {
|
||||
assert!(Agenda::<T>::iter().count() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
on_initialize_periodic_named {
|
||||
let s in 1 .. T::MaxScheduledPerBlock::get();
|
||||
let when = BLOCK_NUMBER.into();
|
||||
fill_schedule::<T>(when, s, true, true, Some(false))?;
|
||||
}: { Scheduler::<T>::on_initialize(BLOCK_NUMBER.into()); }
|
||||
verify {
|
||||
assert_eq!(System::<T>::event_count(), s);
|
||||
for i in 0..s {
|
||||
assert_eq!(Agenda::<T>::get(when + (i + 100).into()).len(), 1 as usize);
|
||||
}
|
||||
}
|
||||
|
||||
on_initialize_periodic {
|
||||
let s in 1 .. T::MaxScheduledPerBlock::get();
|
||||
let when = BLOCK_NUMBER.into();
|
||||
fill_schedule::<T>(when, s, true, false, Some(false))?;
|
||||
}: { Scheduler::<T>::on_initialize(when); }
|
||||
verify {
|
||||
assert_eq!(System::<T>::event_count(), s);
|
||||
for i in 0..s {
|
||||
assert_eq!(Agenda::<T>::get(when + (i + 100).into()).len(), 1 as usize);
|
||||
}
|
||||
}
|
||||
|
||||
on_initialize_named {
|
||||
let s in 1 .. T::MaxScheduledPerBlock::get();
|
||||
let when = BLOCK_NUMBER.into();
|
||||
fill_schedule::<T>(when, s, false, true, Some(false))?;
|
||||
}: { Scheduler::<T>::on_initialize(BLOCK_NUMBER.into()); }
|
||||
verify {
|
||||
assert_eq!(System::<T>::event_count(), s);
|
||||
assert!(Agenda::<T>::iter().count() == 0);
|
||||
}
|
||||
|
||||
on_initialize {
|
||||
let s in 1 .. T::MaxScheduledPerBlock::get();
|
||||
let when = BLOCK_NUMBER.into();
|
||||
fill_schedule::<T>(when, s, false, false, Some(false))?;
|
||||
}: { Scheduler::<T>::on_initialize(BLOCK_NUMBER.into()); }
|
||||
verify {
|
||||
assert_eq!(System::<T>::event_count(), s);
|
||||
assert!(Agenda::<T>::iter().count() == 0);
|
||||
}
|
||||
|
||||
schedule {
|
||||
let s in 0 .. T::MaxScheduledPerBlock::get();
|
||||
let when = BLOCK_NUMBER.into();
|
||||
let periodic = Some((T::BlockNumber::one(), 100));
|
||||
let priority = 0;
|
||||
// Essentially a no-op call.
|
||||
let call = Box::new(frame_system::Call::set_storage { items: vec![] }.into());
|
||||
let inner_call = frame_system::Call::set_storage { items: vec![] }.into();
|
||||
let call = Box::new(CallOrHashOf::<T>::Value(inner_call));
|
||||
|
||||
fill_schedule::<T>(when, s)?;
|
||||
fill_schedule::<T>(when, s, true, true, Some(false))?;
|
||||
}: _(RawOrigin::Root, when, periodic, priority, call)
|
||||
verify {
|
||||
ensure!(
|
||||
@@ -73,7 +222,7 @@ benchmarks! {
|
||||
let s in 1 .. T::MaxScheduledPerBlock::get();
|
||||
let when = BLOCK_NUMBER.into();
|
||||
|
||||
fill_schedule::<T>(when, s)?;
|
||||
fill_schedule::<T>(when, s, true, true, Some(false))?;
|
||||
assert_eq!(Agenda::<T>::get(when).len(), s as usize);
|
||||
}: _(RawOrigin::Root, when, 0)
|
||||
verify {
|
||||
@@ -95,9 +244,10 @@ benchmarks! {
|
||||
let periodic = Some((T::BlockNumber::one(), 100));
|
||||
let priority = 0;
|
||||
// Essentially a no-op call.
|
||||
let call = Box::new(frame_system::Call::set_storage { items: vec![] }.into());
|
||||
let inner_call = frame_system::Call::set_storage { items: vec![] }.into();
|
||||
let call = Box::new(CallOrHashOf::<T>::Value(inner_call));
|
||||
|
||||
fill_schedule::<T>(when, s)?;
|
||||
fill_schedule::<T>(when, s, true, true, Some(false))?;
|
||||
}: _(RawOrigin::Root, id, when, periodic, priority, call)
|
||||
verify {
|
||||
ensure!(
|
||||
@@ -110,7 +260,7 @@ benchmarks! {
|
||||
let s in 1 .. T::MaxScheduledPerBlock::get();
|
||||
let when = BLOCK_NUMBER.into();
|
||||
|
||||
fill_schedule::<T>(when, s)?;
|
||||
fill_schedule::<T>(when, s, true, true, Some(false))?;
|
||||
}: _(RawOrigin::Root, 0.encode())
|
||||
verify {
|
||||
ensure!(
|
||||
@@ -124,21 +274,5 @@ benchmarks! {
|
||||
);
|
||||
}
|
||||
|
||||
// TODO [#7141]: Make this more complex and flexible so it can be used in automation.
|
||||
#[extra]
|
||||
on_initialize {
|
||||
let s in 0 .. T::MaxScheduledPerBlock::get();
|
||||
let when = BLOCK_NUMBER.into();
|
||||
fill_schedule::<T>(when, s)?;
|
||||
}: { Scheduler::<T>::on_initialize(BLOCK_NUMBER.into()); }
|
||||
verify {
|
||||
assert_eq!(System::<T>::event_count(), s);
|
||||
// Next block should have all the schedules again
|
||||
ensure!(
|
||||
Agenda::<T>::get(when + T::BlockNumber::one()).len() == s as usize,
|
||||
"didn't append schedule"
|
||||
);
|
||||
}
|
||||
|
||||
impl_benchmark_test_suite!(Scheduler, crate::tests::new_test_ext(), crate::tests::Test);
|
||||
impl_benchmark_test_suite!(Scheduler, crate::mock::new_test_ext(), crate::mock::Test);
|
||||
}
|
||||
|
||||
+261
-1116
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,203 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2017-2021 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.
|
||||
|
||||
//! # Scheduler test environment.
|
||||
|
||||
use super::*;
|
||||
|
||||
use crate as scheduler;
|
||||
use frame_support::{
|
||||
ord_parameter_types, parameter_types,
|
||||
traits::{Contains, EnsureOneOf, EqualPrivilegeOnly, OnFinalize, OnInitialize},
|
||||
weights::constants::RocksDbWeight,
|
||||
};
|
||||
use frame_system::{EnsureRoot, EnsureSignedBy};
|
||||
use sp_core::H256;
|
||||
use sp_runtime::{
|
||||
testing::Header,
|
||||
traits::{BlakeTwo256, IdentityLookup},
|
||||
Perbill,
|
||||
};
|
||||
|
||||
// Logger module to track execution.
|
||||
#[frame_support::pallet]
|
||||
pub mod logger {
|
||||
use super::*;
|
||||
use frame_support::pallet_prelude::*;
|
||||
use frame_system::pallet_prelude::*;
|
||||
use std::cell::RefCell;
|
||||
|
||||
thread_local! {
|
||||
static LOG: RefCell<Vec<(OriginCaller, u32)>> = RefCell::new(Vec::new());
|
||||
}
|
||||
pub fn log() -> Vec<(OriginCaller, u32)> {
|
||||
LOG.with(|log| log.borrow().clone())
|
||||
}
|
||||
|
||||
#[pallet::pallet]
|
||||
#[pallet::generate_store(pub(super) trait Store)]
|
||||
pub struct Pallet<T>(PhantomData<T>);
|
||||
|
||||
#[pallet::hooks]
|
||||
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}
|
||||
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config {
|
||||
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
|
||||
}
|
||||
|
||||
#[pallet::event]
|
||||
#[pallet::generate_deposit(pub(super) fn deposit_event)]
|
||||
pub enum Event<T: Config> {
|
||||
Logged(u32, Weight),
|
||||
}
|
||||
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T>
|
||||
where
|
||||
<T as system::Config>::Origin: OriginTrait<PalletsOrigin = OriginCaller>,
|
||||
{
|
||||
#[pallet::weight(*weight)]
|
||||
pub fn log(origin: OriginFor<T>, i: u32, weight: Weight) -> DispatchResult {
|
||||
Self::deposit_event(Event::Logged(i, weight));
|
||||
LOG.with(|log| {
|
||||
log.borrow_mut().push((origin.caller().clone(), i));
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[pallet::weight(*weight)]
|
||||
pub fn log_without_filter(origin: OriginFor<T>, i: u32, weight: Weight) -> DispatchResult {
|
||||
Self::deposit_event(Event::Logged(i, weight));
|
||||
LOG.with(|log| {
|
||||
log.borrow_mut().push((origin.caller().clone(), i));
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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>},
|
||||
Logger: logger::{Pallet, Call, Event<T>},
|
||||
Scheduler: scheduler::{Pallet, Call, Storage, Event<T>},
|
||||
Preimage: pallet_preimage::{Pallet, Call, Storage, Event<T>},
|
||||
}
|
||||
);
|
||||
|
||||
// Scheduler must dispatch with root and no filter, this tests base filter is indeed not used.
|
||||
pub struct BaseFilter;
|
||||
impl Contains<Call> for BaseFilter {
|
||||
fn contains(call: &Call) -> bool {
|
||||
!matches!(call, Call::Logger(LoggerCall::log { .. }))
|
||||
}
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const BlockHashCount: u64 = 250;
|
||||
pub BlockWeights: frame_system::limits::BlockWeights =
|
||||
frame_system::limits::BlockWeights::simple_max(2_000_000_000_000);
|
||||
}
|
||||
impl system::Config for Test {
|
||||
type BaseCallFilter = BaseFilter;
|
||||
type BlockWeights = ();
|
||||
type BlockLength = ();
|
||||
type DbWeight = RocksDbWeight;
|
||||
type Origin = Origin;
|
||||
type Call = Call;
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = H256;
|
||||
type Hashing = BlakeTwo256;
|
||||
type AccountId = u64;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Header = Header;
|
||||
type Event = Event;
|
||||
type BlockHashCount = BlockHashCount;
|
||||
type Version = ();
|
||||
type PalletInfo = PalletInfo;
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
type SystemWeightInfo = ();
|
||||
type SS58Prefix = ();
|
||||
type OnSetCode = ();
|
||||
type MaxConsumers = frame_support::traits::ConstU32<16>;
|
||||
}
|
||||
impl logger::Config for Test {
|
||||
type Event = Event;
|
||||
}
|
||||
parameter_types! {
|
||||
pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * BlockWeights::get().max_block;
|
||||
pub const MaxScheduledPerBlock: u32 = 10;
|
||||
pub const MaxSize: u32 = 1024;
|
||||
pub const NoPreimagePostponement: Option<u64> = Some(2);
|
||||
}
|
||||
ord_parameter_types! {
|
||||
pub const One: u64 = 1;
|
||||
}
|
||||
|
||||
impl pallet_preimage::Config for Test {
|
||||
type Event = Event;
|
||||
type WeightInfo = ();
|
||||
type Currency = ();
|
||||
type ManagerOrigin = EnsureRoot<u64>;
|
||||
type MaxSize = MaxSize;
|
||||
type BaseDeposit = ();
|
||||
type ByteDeposit = ();
|
||||
}
|
||||
|
||||
impl Config for Test {
|
||||
type Event = Event;
|
||||
type Origin = Origin;
|
||||
type PalletsOrigin = OriginCaller;
|
||||
type Call = Call;
|
||||
type MaximumWeight = MaximumSchedulerWeight;
|
||||
type ScheduleOrigin = EnsureOneOf<EnsureRoot<u64>, EnsureSignedBy<One, u64>>;
|
||||
type MaxScheduledPerBlock = MaxScheduledPerBlock;
|
||||
type WeightInfo = ();
|
||||
type OriginPrivilegeCmp = EqualPrivilegeOnly;
|
||||
type PreimageProvider = Preimage;
|
||||
type NoPreimagePostponement = NoPreimagePostponement;
|
||||
}
|
||||
|
||||
pub type LoggerCall = logger::Call<Test>;
|
||||
|
||||
pub fn new_test_ext() -> sp_io::TestExternalities {
|
||||
let t = system::GenesisConfig::default().build_storage::<Test>().unwrap();
|
||||
t.into()
|
||||
}
|
||||
|
||||
pub fn run_to_block(n: u64) {
|
||||
while System::block_number() < n {
|
||||
Scheduler::on_finalize(System::block_number());
|
||||
System::set_block_number(System::block_number() + 1);
|
||||
Scheduler::on_initialize(System::block_number());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn root() -> OriginCaller {
|
||||
system::RawOrigin::Root.into()
|
||||
}
|
||||
@@ -0,0 +1,901 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2017-2021 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.
|
||||
|
||||
//! # Scheduler tests.
|
||||
|
||||
use super::*;
|
||||
use crate::mock::{logger, new_test_ext, root, run_to_block, Call, LoggerCall, Scheduler, Test, *};
|
||||
use frame_support::{
|
||||
assert_err, assert_noop, assert_ok,
|
||||
traits::{Contains, OnInitialize, PreimageProvider},
|
||||
Hashable,
|
||||
};
|
||||
use sp_runtime::traits::Hash;
|
||||
use substrate_test_utils::assert_eq_uvec;
|
||||
|
||||
#[test]
|
||||
fn basic_scheduling_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let call = Call::Logger(LoggerCall::log { i: 42, weight: 1000 });
|
||||
assert!(!<Test as frame_system::Config>::BaseCallFilter::contains(&call));
|
||||
assert_ok!(Scheduler::do_schedule(DispatchTime::At(4), None, 127, root(), call.into()));
|
||||
run_to_block(3);
|
||||
assert!(logger::log().is_empty());
|
||||
run_to_block(4);
|
||||
assert_eq!(logger::log(), vec![(root(), 42u32)]);
|
||||
run_to_block(100);
|
||||
assert_eq!(logger::log(), vec![(root(), 42u32)]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scheduling_with_preimages_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let call = Call::Logger(LoggerCall::log { i: 42, weight: 1000 });
|
||||
let hash = <Test as frame_system::Config>::Hashing::hash_of(&call);
|
||||
let hashed = MaybeHashed::Hash(hash.clone());
|
||||
assert_ok!(Preimage::note_preimage(Origin::signed(0), call.encode()));
|
||||
assert_ok!(Scheduler::do_schedule(DispatchTime::At(4), None, 127, root(), hashed));
|
||||
assert!(Preimage::preimage_requested(&hash));
|
||||
run_to_block(3);
|
||||
assert!(logger::log().is_empty());
|
||||
run_to_block(4);
|
||||
assert!(!Preimage::have_preimage(&hash));
|
||||
assert!(!Preimage::preimage_requested(&hash));
|
||||
assert_eq!(logger::log(), vec![(root(), 42u32)]);
|
||||
run_to_block(100);
|
||||
assert_eq!(logger::log(), vec![(root(), 42u32)]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scheduling_with_preimage_postpones_correctly() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let call = Call::Logger(LoggerCall::log { i: 42, weight: 1000 });
|
||||
let hash = <Test as frame_system::Config>::Hashing::hash_of(&call);
|
||||
let hashed = MaybeHashed::Hash(hash.clone());
|
||||
|
||||
assert_ok!(Scheduler::do_schedule(DispatchTime::At(4), None, 127, root(), hashed));
|
||||
assert!(Preimage::preimage_requested(&hash));
|
||||
|
||||
run_to_block(4);
|
||||
// #4 empty due to no preimage
|
||||
assert!(logger::log().is_empty());
|
||||
|
||||
// Register preimage.
|
||||
assert_ok!(Preimage::note_preimage(Origin::signed(0), call.encode()));
|
||||
|
||||
run_to_block(5);
|
||||
// #5 empty since postponement is 2 blocks.
|
||||
assert!(logger::log().is_empty());
|
||||
|
||||
run_to_block(6);
|
||||
// #6 is good.
|
||||
assert_eq!(logger::log(), vec![(root(), 42u32)]);
|
||||
assert!(!Preimage::have_preimage(&hash));
|
||||
assert!(!Preimage::preimage_requested(&hash));
|
||||
|
||||
run_to_block(100);
|
||||
assert_eq!(logger::log(), vec![(root(), 42u32)]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn schedule_after_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
run_to_block(2);
|
||||
let call = Call::Logger(LoggerCall::log { i: 42, weight: 1000 });
|
||||
assert!(!<Test as frame_system::Config>::BaseCallFilter::contains(&call));
|
||||
// This will schedule the call 3 blocks after the next block... so block 3 + 3 = 6
|
||||
assert_ok!(Scheduler::do_schedule(DispatchTime::After(3), None, 127, root(), call.into()));
|
||||
run_to_block(5);
|
||||
assert!(logger::log().is_empty());
|
||||
run_to_block(6);
|
||||
assert_eq!(logger::log(), vec![(root(), 42u32)]);
|
||||
run_to_block(100);
|
||||
assert_eq!(logger::log(), vec![(root(), 42u32)]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn schedule_after_zero_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
run_to_block(2);
|
||||
let call = Call::Logger(LoggerCall::log { i: 42, weight: 1000 });
|
||||
assert!(!<Test as frame_system::Config>::BaseCallFilter::contains(&call));
|
||||
assert_ok!(Scheduler::do_schedule(DispatchTime::After(0), None, 127, root(), call.into()));
|
||||
// Will trigger on the next block.
|
||||
run_to_block(3);
|
||||
assert_eq!(logger::log(), vec![(root(), 42u32)]);
|
||||
run_to_block(100);
|
||||
assert_eq!(logger::log(), vec![(root(), 42u32)]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn periodic_scheduling_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
// at #4, every 3 blocks, 3 times.
|
||||
assert_ok!(Scheduler::do_schedule(
|
||||
DispatchTime::At(4),
|
||||
Some((3, 3)),
|
||||
127,
|
||||
root(),
|
||||
Call::Logger(logger::Call::log { i: 42, weight: 1000 }).into()
|
||||
));
|
||||
run_to_block(3);
|
||||
assert!(logger::log().is_empty());
|
||||
run_to_block(4);
|
||||
assert_eq!(logger::log(), vec![(root(), 42u32)]);
|
||||
run_to_block(6);
|
||||
assert_eq!(logger::log(), vec![(root(), 42u32)]);
|
||||
run_to_block(7);
|
||||
assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]);
|
||||
run_to_block(9);
|
||||
assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]);
|
||||
run_to_block(10);
|
||||
assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32), (root(), 42u32)]);
|
||||
run_to_block(100);
|
||||
assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32), (root(), 42u32)]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reschedule_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let call = Call::Logger(LoggerCall::log { i: 42, weight: 1000 });
|
||||
assert!(!<Test as frame_system::Config>::BaseCallFilter::contains(&call));
|
||||
assert_eq!(
|
||||
Scheduler::do_schedule(DispatchTime::At(4), None, 127, root(), call.into()).unwrap(),
|
||||
(4, 0)
|
||||
);
|
||||
|
||||
run_to_block(3);
|
||||
assert!(logger::log().is_empty());
|
||||
|
||||
assert_eq!(Scheduler::do_reschedule((4, 0), DispatchTime::At(6)).unwrap(), (6, 0));
|
||||
|
||||
assert_noop!(
|
||||
Scheduler::do_reschedule((6, 0), DispatchTime::At(6)),
|
||||
Error::<Test>::RescheduleNoChange
|
||||
);
|
||||
|
||||
run_to_block(4);
|
||||
assert!(logger::log().is_empty());
|
||||
|
||||
run_to_block(6);
|
||||
assert_eq!(logger::log(), vec![(root(), 42u32)]);
|
||||
|
||||
run_to_block(100);
|
||||
assert_eq!(logger::log(), vec![(root(), 42u32)]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reschedule_named_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let call = Call::Logger(LoggerCall::log { i: 42, weight: 1000 });
|
||||
assert!(!<Test as frame_system::Config>::BaseCallFilter::contains(&call));
|
||||
assert_eq!(
|
||||
Scheduler::do_schedule_named(
|
||||
1u32.encode(),
|
||||
DispatchTime::At(4),
|
||||
None,
|
||||
127,
|
||||
root(),
|
||||
call.into(),
|
||||
)
|
||||
.unwrap(),
|
||||
(4, 0)
|
||||
);
|
||||
|
||||
run_to_block(3);
|
||||
assert!(logger::log().is_empty());
|
||||
|
||||
assert_eq!(
|
||||
Scheduler::do_reschedule_named(1u32.encode(), DispatchTime::At(6)).unwrap(),
|
||||
(6, 0)
|
||||
);
|
||||
|
||||
assert_noop!(
|
||||
Scheduler::do_reschedule_named(1u32.encode(), DispatchTime::At(6)),
|
||||
Error::<Test>::RescheduleNoChange
|
||||
);
|
||||
|
||||
run_to_block(4);
|
||||
assert!(logger::log().is_empty());
|
||||
|
||||
run_to_block(6);
|
||||
assert_eq!(logger::log(), vec![(root(), 42u32)]);
|
||||
|
||||
run_to_block(100);
|
||||
assert_eq!(logger::log(), vec![(root(), 42u32)]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reschedule_named_perodic_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let call = Call::Logger(LoggerCall::log { i: 42, weight: 1000 });
|
||||
assert!(!<Test as frame_system::Config>::BaseCallFilter::contains(&call));
|
||||
assert_eq!(
|
||||
Scheduler::do_schedule_named(
|
||||
1u32.encode(),
|
||||
DispatchTime::At(4),
|
||||
Some((3, 3)),
|
||||
127,
|
||||
root(),
|
||||
call.into(),
|
||||
)
|
||||
.unwrap(),
|
||||
(4, 0)
|
||||
);
|
||||
|
||||
run_to_block(3);
|
||||
assert!(logger::log().is_empty());
|
||||
|
||||
assert_eq!(
|
||||
Scheduler::do_reschedule_named(1u32.encode(), DispatchTime::At(5)).unwrap(),
|
||||
(5, 0)
|
||||
);
|
||||
assert_eq!(
|
||||
Scheduler::do_reschedule_named(1u32.encode(), DispatchTime::At(6)).unwrap(),
|
||||
(6, 0)
|
||||
);
|
||||
|
||||
run_to_block(5);
|
||||
assert!(logger::log().is_empty());
|
||||
|
||||
run_to_block(6);
|
||||
assert_eq!(logger::log(), vec![(root(), 42u32)]);
|
||||
|
||||
assert_eq!(
|
||||
Scheduler::do_reschedule_named(1u32.encode(), DispatchTime::At(10)).unwrap(),
|
||||
(10, 0)
|
||||
);
|
||||
|
||||
run_to_block(9);
|
||||
assert_eq!(logger::log(), vec![(root(), 42u32)]);
|
||||
|
||||
run_to_block(10);
|
||||
assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]);
|
||||
|
||||
run_to_block(13);
|
||||
assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32), (root(), 42u32)]);
|
||||
|
||||
run_to_block(100);
|
||||
assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32), (root(), 42u32)]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cancel_named_scheduling_works_with_normal_cancel() {
|
||||
new_test_ext().execute_with(|| {
|
||||
// at #4.
|
||||
Scheduler::do_schedule_named(
|
||||
1u32.encode(),
|
||||
DispatchTime::At(4),
|
||||
None,
|
||||
127,
|
||||
root(),
|
||||
Call::Logger(LoggerCall::log { i: 69, weight: 1000 }).into(),
|
||||
)
|
||||
.unwrap();
|
||||
let i = Scheduler::do_schedule(
|
||||
DispatchTime::At(4),
|
||||
None,
|
||||
127,
|
||||
root(),
|
||||
Call::Logger(LoggerCall::log { i: 42, weight: 1000 }).into(),
|
||||
)
|
||||
.unwrap();
|
||||
run_to_block(3);
|
||||
assert!(logger::log().is_empty());
|
||||
assert_ok!(Scheduler::do_cancel_named(None, 1u32.encode()));
|
||||
assert_ok!(Scheduler::do_cancel(None, i));
|
||||
run_to_block(100);
|
||||
assert!(logger::log().is_empty());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cancel_named_periodic_scheduling_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
// at #4, every 3 blocks, 3 times.
|
||||
Scheduler::do_schedule_named(
|
||||
1u32.encode(),
|
||||
DispatchTime::At(4),
|
||||
Some((3, 3)),
|
||||
127,
|
||||
root(),
|
||||
Call::Logger(LoggerCall::log { i: 42, weight: 1000 }).into(),
|
||||
)
|
||||
.unwrap();
|
||||
// same id results in error.
|
||||
assert!(Scheduler::do_schedule_named(
|
||||
1u32.encode(),
|
||||
DispatchTime::At(4),
|
||||
None,
|
||||
127,
|
||||
root(),
|
||||
Call::Logger(LoggerCall::log { i: 69, weight: 1000 }).into(),
|
||||
)
|
||||
.is_err());
|
||||
// different id is ok.
|
||||
Scheduler::do_schedule_named(
|
||||
2u32.encode(),
|
||||
DispatchTime::At(8),
|
||||
None,
|
||||
127,
|
||||
root(),
|
||||
Call::Logger(LoggerCall::log { i: 69, weight: 1000 }).into(),
|
||||
)
|
||||
.unwrap();
|
||||
run_to_block(3);
|
||||
assert!(logger::log().is_empty());
|
||||
run_to_block(4);
|
||||
assert_eq!(logger::log(), vec![(root(), 42u32)]);
|
||||
run_to_block(6);
|
||||
assert_ok!(Scheduler::do_cancel_named(None, 1u32.encode()));
|
||||
run_to_block(100);
|
||||
assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 69u32)]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scheduler_respects_weight_limits() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_ok!(Scheduler::do_schedule(
|
||||
DispatchTime::At(4),
|
||||
None,
|
||||
127,
|
||||
root(),
|
||||
Call::Logger(LoggerCall::log { i: 42, weight: MaximumSchedulerWeight::get() / 2 })
|
||||
.into(),
|
||||
));
|
||||
assert_ok!(Scheduler::do_schedule(
|
||||
DispatchTime::At(4),
|
||||
None,
|
||||
127,
|
||||
root(),
|
||||
Call::Logger(LoggerCall::log { i: 69, weight: MaximumSchedulerWeight::get() / 2 })
|
||||
.into(),
|
||||
));
|
||||
// 69 and 42 do not fit together
|
||||
run_to_block(4);
|
||||
assert_eq!(logger::log(), vec![(root(), 42u32)]);
|
||||
run_to_block(5);
|
||||
assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 69u32)]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scheduler_respects_hard_deadlines_more() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_ok!(Scheduler::do_schedule(
|
||||
DispatchTime::At(4),
|
||||
None,
|
||||
0,
|
||||
root(),
|
||||
Call::Logger(LoggerCall::log { i: 42, weight: MaximumSchedulerWeight::get() / 2 })
|
||||
.into(),
|
||||
));
|
||||
assert_ok!(Scheduler::do_schedule(
|
||||
DispatchTime::At(4),
|
||||
None,
|
||||
0,
|
||||
root(),
|
||||
Call::Logger(LoggerCall::log { i: 69, weight: MaximumSchedulerWeight::get() / 2 })
|
||||
.into(),
|
||||
));
|
||||
// With base weights, 69 and 42 should not fit together, but do because of hard
|
||||
// deadlines
|
||||
run_to_block(4);
|
||||
assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 69u32)]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scheduler_respects_priority_ordering() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_ok!(Scheduler::do_schedule(
|
||||
DispatchTime::At(4),
|
||||
None,
|
||||
1,
|
||||
root(),
|
||||
Call::Logger(LoggerCall::log { i: 42, weight: MaximumSchedulerWeight::get() / 2 })
|
||||
.into(),
|
||||
));
|
||||
assert_ok!(Scheduler::do_schedule(
|
||||
DispatchTime::At(4),
|
||||
None,
|
||||
0,
|
||||
root(),
|
||||
Call::Logger(LoggerCall::log { i: 69, weight: MaximumSchedulerWeight::get() / 2 })
|
||||
.into(),
|
||||
));
|
||||
run_to_block(4);
|
||||
assert_eq!(logger::log(), vec![(root(), 69u32), (root(), 42u32)]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scheduler_respects_priority_ordering_with_soft_deadlines() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let max_weight = MaximumSchedulerWeight::get() - <() as WeightInfo>::on_initialize(0);
|
||||
let item_weight =
|
||||
<() as WeightInfo>::on_initialize(1) - <() as WeightInfo>::on_initialize(0);
|
||||
assert_ok!(Scheduler::do_schedule(
|
||||
DispatchTime::At(4),
|
||||
None,
|
||||
255,
|
||||
root(),
|
||||
Call::Logger(LoggerCall::log { i: 42, weight: max_weight / 2 - item_weight }).into(),
|
||||
));
|
||||
assert_ok!(Scheduler::do_schedule(
|
||||
DispatchTime::At(4),
|
||||
None,
|
||||
127,
|
||||
root(),
|
||||
Call::Logger(LoggerCall::log { i: 69, weight: max_weight / 2 - item_weight }).into(),
|
||||
));
|
||||
assert_ok!(Scheduler::do_schedule(
|
||||
DispatchTime::At(4),
|
||||
None,
|
||||
126,
|
||||
root(),
|
||||
Call::Logger(LoggerCall::log { i: 2600, weight: max_weight / 2 - item_weight + 1 })
|
||||
.into(),
|
||||
));
|
||||
|
||||
// 2600 does not fit with 69 or 42, but has higher priority, so will go through
|
||||
run_to_block(4);
|
||||
assert_eq!(logger::log(), vec![(root(), 2600u32)]);
|
||||
// 69 and 42 fit together
|
||||
run_to_block(5);
|
||||
assert_eq!(logger::log(), vec![(root(), 2600u32), (root(), 69u32), (root(), 42u32)]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn on_initialize_weight_is_correct() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let base_weight = <() as WeightInfo>::on_initialize(0);
|
||||
let call_weight = MaximumSchedulerWeight::get() / 4;
|
||||
|
||||
// Named
|
||||
assert_ok!(Scheduler::do_schedule_named(
|
||||
1u32.encode(),
|
||||
DispatchTime::At(3),
|
||||
None,
|
||||
255,
|
||||
root(),
|
||||
Call::Logger(LoggerCall::log { i: 3, weight: call_weight + 1 }).into(),
|
||||
));
|
||||
// Anon Periodic
|
||||
assert_ok!(Scheduler::do_schedule(
|
||||
DispatchTime::At(2),
|
||||
Some((1000, 3)),
|
||||
128,
|
||||
root(),
|
||||
Call::Logger(LoggerCall::log { i: 42, weight: call_weight + 2 }).into(),
|
||||
));
|
||||
// Anon
|
||||
assert_ok!(Scheduler::do_schedule(
|
||||
DispatchTime::At(2),
|
||||
None,
|
||||
127,
|
||||
root(),
|
||||
Call::Logger(LoggerCall::log { i: 69, weight: call_weight + 3 }).into(),
|
||||
));
|
||||
// Named Periodic
|
||||
assert_ok!(Scheduler::do_schedule_named(
|
||||
2u32.encode(),
|
||||
DispatchTime::At(1),
|
||||
Some((1000, 3)),
|
||||
126,
|
||||
root(),
|
||||
Call::Logger(LoggerCall::log { i: 2600, weight: call_weight + 4 }).into(),
|
||||
));
|
||||
|
||||
// Will include the named periodic only
|
||||
let actual_weight = Scheduler::on_initialize(1);
|
||||
assert_eq!(
|
||||
actual_weight,
|
||||
base_weight +
|
||||
call_weight + 4 + <() as MarginalWeightInfo>::item(true, true, Some(false))
|
||||
);
|
||||
assert_eq!(logger::log(), vec![(root(), 2600u32)]);
|
||||
|
||||
// Will include anon and anon periodic
|
||||
let actual_weight = Scheduler::on_initialize(2);
|
||||
assert_eq!(
|
||||
actual_weight,
|
||||
base_weight +
|
||||
call_weight + 2 + <() as MarginalWeightInfo>::item(false, false, Some(false)) +
|
||||
call_weight + 3 + <() as MarginalWeightInfo>::item(true, false, Some(false))
|
||||
);
|
||||
assert_eq!(logger::log(), vec![(root(), 2600u32), (root(), 69u32), (root(), 42u32)]);
|
||||
|
||||
// Will include named only
|
||||
let actual_weight = Scheduler::on_initialize(3);
|
||||
assert_eq!(
|
||||
actual_weight,
|
||||
base_weight +
|
||||
call_weight + 1 + <() as MarginalWeightInfo>::item(false, true, Some(false))
|
||||
);
|
||||
assert_eq!(
|
||||
logger::log(),
|
||||
vec![(root(), 2600u32), (root(), 69u32), (root(), 42u32), (root(), 3u32)]
|
||||
);
|
||||
|
||||
// Will contain none
|
||||
let actual_weight = Scheduler::on_initialize(4);
|
||||
assert_eq!(actual_weight, base_weight);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn root_calls_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let call = Box::new(Call::Logger(LoggerCall::log { i: 69, weight: 1000 }).into());
|
||||
let call2 = Box::new(Call::Logger(LoggerCall::log { i: 42, weight: 1000 }).into());
|
||||
assert_ok!(Scheduler::schedule_named(Origin::root(), 1u32.encode(), 4, None, 127, call,));
|
||||
assert_ok!(Scheduler::schedule(Origin::root(), 4, None, 127, call2));
|
||||
run_to_block(3);
|
||||
// Scheduled calls are in the agenda.
|
||||
assert_eq!(Agenda::<Test>::get(4).len(), 2);
|
||||
assert!(logger::log().is_empty());
|
||||
assert_ok!(Scheduler::cancel_named(Origin::root(), 1u32.encode()));
|
||||
assert_ok!(Scheduler::cancel(Origin::root(), 4, 1));
|
||||
// Scheduled calls are made NONE, so should not effect state
|
||||
run_to_block(100);
|
||||
assert!(logger::log().is_empty());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fails_to_schedule_task_in_the_past() {
|
||||
new_test_ext().execute_with(|| {
|
||||
run_to_block(3);
|
||||
|
||||
let call1 = Box::new(Call::Logger(LoggerCall::log { i: 69, weight: 1000 }).into());
|
||||
let call2 = Box::new(Call::Logger(LoggerCall::log { i: 42, weight: 1000 }).into());
|
||||
let call3 = Box::new(Call::Logger(LoggerCall::log { i: 42, weight: 1000 }).into());
|
||||
|
||||
assert_err!(
|
||||
Scheduler::schedule_named(Origin::root(), 1u32.encode(), 2, None, 127, call1),
|
||||
Error::<Test>::TargetBlockNumberInPast,
|
||||
);
|
||||
|
||||
assert_err!(
|
||||
Scheduler::schedule(Origin::root(), 2, None, 127, call2),
|
||||
Error::<Test>::TargetBlockNumberInPast,
|
||||
);
|
||||
|
||||
assert_err!(
|
||||
Scheduler::schedule(Origin::root(), 3, None, 127, call3),
|
||||
Error::<Test>::TargetBlockNumberInPast,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_use_orign() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let call = Box::new(Call::Logger(LoggerCall::log { i: 69, weight: 1000 }).into());
|
||||
let call2 = Box::new(Call::Logger(LoggerCall::log { i: 42, weight: 1000 }).into());
|
||||
assert_ok!(Scheduler::schedule_named(
|
||||
system::RawOrigin::Signed(1).into(),
|
||||
1u32.encode(),
|
||||
4,
|
||||
None,
|
||||
127,
|
||||
call,
|
||||
));
|
||||
assert_ok!(Scheduler::schedule(system::RawOrigin::Signed(1).into(), 4, None, 127, call2,));
|
||||
run_to_block(3);
|
||||
// Scheduled calls are in the agenda.
|
||||
assert_eq!(Agenda::<Test>::get(4).len(), 2);
|
||||
assert!(logger::log().is_empty());
|
||||
assert_ok!(Scheduler::cancel_named(system::RawOrigin::Signed(1).into(), 1u32.encode()));
|
||||
assert_ok!(Scheduler::cancel(system::RawOrigin::Signed(1).into(), 4, 1));
|
||||
// Scheduled calls are made NONE, so should not effect state
|
||||
run_to_block(100);
|
||||
assert!(logger::log().is_empty());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_check_orign() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let call = Box::new(Call::Logger(LoggerCall::log { i: 69, weight: 1000 }).into());
|
||||
let call2 = Box::new(Call::Logger(LoggerCall::log { i: 42, weight: 1000 }).into());
|
||||
assert_noop!(
|
||||
Scheduler::schedule_named(
|
||||
system::RawOrigin::Signed(2).into(),
|
||||
1u32.encode(),
|
||||
4,
|
||||
None,
|
||||
127,
|
||||
call
|
||||
),
|
||||
BadOrigin
|
||||
);
|
||||
assert_noop!(
|
||||
Scheduler::schedule(system::RawOrigin::Signed(2).into(), 4, None, 127, call2),
|
||||
BadOrigin
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_check_orign_for_cancel() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let call =
|
||||
Box::new(Call::Logger(LoggerCall::log_without_filter { i: 69, weight: 1000 }).into());
|
||||
let call2 =
|
||||
Box::new(Call::Logger(LoggerCall::log_without_filter { i: 42, weight: 1000 }).into());
|
||||
assert_ok!(Scheduler::schedule_named(
|
||||
system::RawOrigin::Signed(1).into(),
|
||||
1u32.encode(),
|
||||
4,
|
||||
None,
|
||||
127,
|
||||
call,
|
||||
));
|
||||
assert_ok!(Scheduler::schedule(system::RawOrigin::Signed(1).into(), 4, None, 127, call2,));
|
||||
run_to_block(3);
|
||||
// Scheduled calls are in the agenda.
|
||||
assert_eq!(Agenda::<Test>::get(4).len(), 2);
|
||||
assert!(logger::log().is_empty());
|
||||
assert_noop!(
|
||||
Scheduler::cancel_named(system::RawOrigin::Signed(2).into(), 1u32.encode()),
|
||||
BadOrigin
|
||||
);
|
||||
assert_noop!(Scheduler::cancel(system::RawOrigin::Signed(2).into(), 4, 1), BadOrigin);
|
||||
assert_noop!(
|
||||
Scheduler::cancel_named(system::RawOrigin::Root.into(), 1u32.encode()),
|
||||
BadOrigin
|
||||
);
|
||||
assert_noop!(Scheduler::cancel(system::RawOrigin::Root.into(), 4, 1), BadOrigin);
|
||||
run_to_block(5);
|
||||
assert_eq!(
|
||||
logger::log(),
|
||||
vec![
|
||||
(system::RawOrigin::Signed(1).into(), 69u32),
|
||||
(system::RawOrigin::Signed(1).into(), 42u32)
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn migration_to_v3_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
for i in 0..3u64 {
|
||||
let k = i.twox_64_concat();
|
||||
let old = vec![
|
||||
Some(ScheduledV1 {
|
||||
maybe_id: None,
|
||||
priority: i as u8 + 10,
|
||||
call: Call::Logger(LoggerCall::log { i: 96, weight: 100 }),
|
||||
maybe_periodic: None,
|
||||
}),
|
||||
None,
|
||||
Some(ScheduledV1 {
|
||||
maybe_id: Some(b"test".to_vec()),
|
||||
priority: 123,
|
||||
call: Call::Logger(LoggerCall::log { i: 69, weight: 1000 }),
|
||||
maybe_periodic: Some((456u64, 10)),
|
||||
}),
|
||||
];
|
||||
frame_support::migration::put_storage_value(b"Scheduler", b"Agenda", &k, old);
|
||||
}
|
||||
|
||||
assert_eq!(StorageVersion::<Test>::get(), Releases::V1);
|
||||
|
||||
assert!(Scheduler::migrate_v1_to_v3());
|
||||
|
||||
assert_eq_uvec!(
|
||||
Agenda::<Test>::iter().collect::<Vec<_>>(),
|
||||
vec![
|
||||
(
|
||||
0,
|
||||
vec![
|
||||
Some(ScheduledV3Of::<Test> {
|
||||
maybe_id: None,
|
||||
priority: 10,
|
||||
call: Call::Logger(LoggerCall::log { i: 96, weight: 100 }).into(),
|
||||
maybe_periodic: None,
|
||||
origin: root(),
|
||||
_phantom: PhantomData::<u64>::default(),
|
||||
}),
|
||||
None,
|
||||
Some(ScheduledV3Of::<Test> {
|
||||
maybe_id: Some(b"test".to_vec()),
|
||||
priority: 123,
|
||||
call: Call::Logger(LoggerCall::log { i: 69, weight: 1000 }).into(),
|
||||
maybe_periodic: Some((456u64, 10)),
|
||||
origin: root(),
|
||||
_phantom: PhantomData::<u64>::default(),
|
||||
}),
|
||||
]
|
||||
),
|
||||
(
|
||||
1,
|
||||
vec![
|
||||
Some(ScheduledV3Of::<Test> {
|
||||
maybe_id: None,
|
||||
priority: 11,
|
||||
call: Call::Logger(LoggerCall::log { i: 96, weight: 100 }).into(),
|
||||
maybe_periodic: None,
|
||||
origin: root(),
|
||||
_phantom: PhantomData::<u64>::default(),
|
||||
}),
|
||||
None,
|
||||
Some(ScheduledV3Of::<Test> {
|
||||
maybe_id: Some(b"test".to_vec()),
|
||||
priority: 123,
|
||||
call: Call::Logger(LoggerCall::log { i: 69, weight: 1000 }).into(),
|
||||
maybe_periodic: Some((456u64, 10)),
|
||||
origin: root(),
|
||||
_phantom: PhantomData::<u64>::default(),
|
||||
}),
|
||||
]
|
||||
),
|
||||
(
|
||||
2,
|
||||
vec![
|
||||
Some(ScheduledV3Of::<Test> {
|
||||
maybe_id: None,
|
||||
priority: 12,
|
||||
call: Call::Logger(LoggerCall::log { i: 96, weight: 100 }).into(),
|
||||
maybe_periodic: None,
|
||||
origin: root(),
|
||||
_phantom: PhantomData::<u64>::default(),
|
||||
}),
|
||||
None,
|
||||
Some(ScheduledV3Of::<Test> {
|
||||
maybe_id: Some(b"test".to_vec()),
|
||||
priority: 123,
|
||||
call: Call::Logger(LoggerCall::log { i: 69, weight: 1000 }).into(),
|
||||
maybe_periodic: Some((456u64, 10)),
|
||||
origin: root(),
|
||||
_phantom: PhantomData::<u64>::default(),
|
||||
}),
|
||||
]
|
||||
)
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(StorageVersion::<Test>::get(), Releases::V3);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_migrate_origin() {
|
||||
new_test_ext().execute_with(|| {
|
||||
for i in 0..3u64 {
|
||||
let k = i.twox_64_concat();
|
||||
let old: Vec<Option<Scheduled<CallOrHashOf<Test>, u64, u32, u64>>> = vec![
|
||||
Some(Scheduled {
|
||||
maybe_id: None,
|
||||
priority: i as u8 + 10,
|
||||
call: Call::Logger(LoggerCall::log { i: 96, weight: 100 }).into(),
|
||||
origin: 3u32,
|
||||
maybe_periodic: None,
|
||||
_phantom: Default::default(),
|
||||
}),
|
||||
None,
|
||||
Some(Scheduled {
|
||||
maybe_id: Some(b"test".to_vec()),
|
||||
priority: 123,
|
||||
origin: 2u32,
|
||||
call: Call::Logger(LoggerCall::log { i: 69, weight: 1000 }).into(),
|
||||
maybe_periodic: Some((456u64, 10)),
|
||||
_phantom: Default::default(),
|
||||
}),
|
||||
];
|
||||
frame_support::migration::put_storage_value(b"Scheduler", b"Agenda", &k, old);
|
||||
}
|
||||
|
||||
impl Into<OriginCaller> for u32 {
|
||||
fn into(self) -> OriginCaller {
|
||||
match self {
|
||||
3u32 => system::RawOrigin::Root.into(),
|
||||
2u32 => system::RawOrigin::None.into(),
|
||||
_ => unreachable!("test make no use of it"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Scheduler::migrate_origin::<u32>();
|
||||
|
||||
assert_eq_uvec!(
|
||||
Agenda::<Test>::iter().collect::<Vec<_>>(),
|
||||
vec![
|
||||
(
|
||||
0,
|
||||
vec![
|
||||
Some(ScheduledV2::<CallOrHashOf<Test>, u64, OriginCaller, u64> {
|
||||
maybe_id: None,
|
||||
priority: 10,
|
||||
call: Call::Logger(LoggerCall::log { i: 96, weight: 100 }).into(),
|
||||
maybe_periodic: None,
|
||||
origin: system::RawOrigin::Root.into(),
|
||||
_phantom: PhantomData::<u64>::default(),
|
||||
}),
|
||||
None,
|
||||
Some(ScheduledV2 {
|
||||
maybe_id: Some(b"test".to_vec()),
|
||||
priority: 123,
|
||||
call: Call::Logger(LoggerCall::log { i: 69, weight: 1000 }).into(),
|
||||
maybe_periodic: Some((456u64, 10)),
|
||||
origin: system::RawOrigin::None.into(),
|
||||
_phantom: PhantomData::<u64>::default(),
|
||||
}),
|
||||
]
|
||||
),
|
||||
(
|
||||
1,
|
||||
vec![
|
||||
Some(ScheduledV2 {
|
||||
maybe_id: None,
|
||||
priority: 11,
|
||||
call: Call::Logger(LoggerCall::log { i: 96, weight: 100 }).into(),
|
||||
maybe_periodic: None,
|
||||
origin: system::RawOrigin::Root.into(),
|
||||
_phantom: PhantomData::<u64>::default(),
|
||||
}),
|
||||
None,
|
||||
Some(ScheduledV2 {
|
||||
maybe_id: Some(b"test".to_vec()),
|
||||
priority: 123,
|
||||
call: Call::Logger(LoggerCall::log { i: 69, weight: 1000 }).into(),
|
||||
maybe_periodic: Some((456u64, 10)),
|
||||
origin: system::RawOrigin::None.into(),
|
||||
_phantom: PhantomData::<u64>::default(),
|
||||
}),
|
||||
]
|
||||
),
|
||||
(
|
||||
2,
|
||||
vec![
|
||||
Some(ScheduledV2 {
|
||||
maybe_id: None,
|
||||
priority: 12,
|
||||
call: Call::Logger(LoggerCall::log { i: 96, weight: 100 }).into(),
|
||||
maybe_periodic: None,
|
||||
origin: system::RawOrigin::Root.into(),
|
||||
_phantom: PhantomData::<u64>::default(),
|
||||
}),
|
||||
None,
|
||||
Some(ScheduledV2 {
|
||||
maybe_id: Some(b"test".to_vec()),
|
||||
priority: 123,
|
||||
call: Call::Logger(LoggerCall::log { i: 69, weight: 1000 }).into(),
|
||||
maybe_periodic: Some((456u64, 10)),
|
||||
origin: system::RawOrigin::None.into(),
|
||||
_phantom: PhantomData::<u64>::default(),
|
||||
}),
|
||||
]
|
||||
)
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -18,7 +18,7 @@
|
||||
//! Autogenerated weights for pallet_scheduler
|
||||
//!
|
||||
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
|
||||
//! DATE: 2021-08-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
|
||||
//! DATE: 2021-12-10, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
|
||||
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128
|
||||
|
||||
// Executed Command:
|
||||
@@ -35,7 +35,6 @@
|
||||
// --output=./frame/scheduler/src/weights.rs
|
||||
// --template=./.maintain/frame-weight-template.hbs
|
||||
|
||||
|
||||
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||
#![allow(unused_parens)]
|
||||
#![allow(unused_imports)]
|
||||
@@ -45,6 +44,16 @@ use sp_std::marker::PhantomData;
|
||||
|
||||
/// Weight functions needed for pallet_scheduler.
|
||||
pub trait WeightInfo {
|
||||
fn on_initialize_periodic_named_resolved(s: u32, ) -> Weight;
|
||||
fn on_initialize_named_resolved(s: u32, ) -> Weight;
|
||||
fn on_initialize_periodic_resolved(s: u32, ) -> Weight;
|
||||
fn on_initialize_resolved(s: u32, ) -> Weight;
|
||||
fn on_initialize_named_aborted(s: u32, ) -> Weight;
|
||||
fn on_initialize_aborted(s: u32, ) -> Weight;
|
||||
fn on_initialize_periodic_named(s: u32, ) -> Weight;
|
||||
fn on_initialize_periodic(s: u32, ) -> Weight;
|
||||
fn on_initialize_named(s: u32, ) -> Weight;
|
||||
fn on_initialize(s: u32, ) -> Weight;
|
||||
fn schedule(s: u32, ) -> Weight;
|
||||
fn cancel(s: u32, ) -> Weight;
|
||||
fn schedule_named(s: u32, ) -> Weight;
|
||||
@@ -54,38 +63,149 @@ pub trait WeightInfo {
|
||||
/// Weights for pallet_scheduler using the Substrate node and recommended hardware.
|
||||
pub struct SubstrateWeight<T>(PhantomData<T>);
|
||||
impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
||||
// Storage: Scheduler Agenda (r:2 w:2)
|
||||
// Storage: Preimage PreimageFor (r:1 w:1)
|
||||
// Storage: Preimage StatusFor (r:1 w:1)
|
||||
// Storage: Scheduler Lookup (r:0 w:1)
|
||||
fn on_initialize_periodic_named_resolved(s: u32, ) -> Weight {
|
||||
(8_183_000 as Weight)
|
||||
// Standard Error: 36_000
|
||||
.saturating_add((34_670_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads((3 as Weight).saturating_mul(s as Weight)))
|
||||
.saturating_add(T::DbWeight::get().writes(1 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes((4 as Weight).saturating_mul(s as Weight)))
|
||||
}
|
||||
// Storage: Scheduler Agenda (r:1 w:1)
|
||||
// Storage: Preimage PreimageFor (r:1 w:1)
|
||||
// Storage: Preimage StatusFor (r:1 w:1)
|
||||
// Storage: Scheduler Lookup (r:0 w:1)
|
||||
fn on_initialize_named_resolved(s: u32, ) -> Weight {
|
||||
(11_520_000 as Weight)
|
||||
// Standard Error: 30_000
|
||||
.saturating_add((26_386_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(s as Weight)))
|
||||
.saturating_add(T::DbWeight::get().writes(1 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes((3 as Weight).saturating_mul(s as Weight)))
|
||||
}
|
||||
// Storage: Scheduler Agenda (r:2 w:2)
|
||||
// Storage: Preimage PreimageFor (r:1 w:1)
|
||||
// Storage: Preimage StatusFor (r:1 w:1)
|
||||
fn on_initialize_periodic_resolved(s: u32, ) -> Weight {
|
||||
(8_222_000 as Weight)
|
||||
// Standard Error: 33_000
|
||||
.saturating_add((28_925_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads((3 as Weight).saturating_mul(s as Weight)))
|
||||
.saturating_add(T::DbWeight::get().writes(1 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes((3 as Weight).saturating_mul(s as Weight)))
|
||||
}
|
||||
// Storage: Scheduler Agenda (r:1 w:1)
|
||||
// Storage: Preimage PreimageFor (r:1 w:1)
|
||||
// Storage: Preimage StatusFor (r:1 w:1)
|
||||
fn on_initialize_resolved(s: u32, ) -> Weight {
|
||||
(11_610_000 as Weight)
|
||||
// Standard Error: 26_000
|
||||
.saturating_add((23_857_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(s as Weight)))
|
||||
.saturating_add(T::DbWeight::get().writes(1 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes((2 as Weight).saturating_mul(s as Weight)))
|
||||
}
|
||||
// Storage: Scheduler Agenda (r:2 w:2)
|
||||
// Storage: Preimage PreimageFor (r:1 w:0)
|
||||
// Storage: Scheduler Lookup (r:0 w:1)
|
||||
fn on_initialize_named_aborted(s: u32, ) -> Weight {
|
||||
(11_067_000 as Weight)
|
||||
// Standard Error: 15_000
|
||||
.saturating_add((11_728_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(2 as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(s as Weight)))
|
||||
.saturating_add(T::DbWeight::get().writes(2 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(s as Weight)))
|
||||
}
|
||||
// Storage: Scheduler Agenda (r:2 w:2)
|
||||
// Storage: Preimage PreimageFor (r:1 w:0)
|
||||
fn on_initialize_aborted(s: u32, ) -> Weight {
|
||||
(13_045_000 as Weight)
|
||||
// Standard Error: 5_000
|
||||
.saturating_add((6_378_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(2 as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(s as Weight)))
|
||||
.saturating_add(T::DbWeight::get().writes(2 as Weight))
|
||||
}
|
||||
// Storage: Scheduler Agenda (r:2 w:2)
|
||||
// Storage: Scheduler Lookup (r:0 w:1)
|
||||
fn on_initialize_periodic_named(s: u32, ) -> Weight {
|
||||
(13_496_000 as Weight)
|
||||
// Standard Error: 27_000
|
||||
.saturating_add((17_932_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(s as Weight)))
|
||||
.saturating_add(T::DbWeight::get().writes(1 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes((2 as Weight).saturating_mul(s as Weight)))
|
||||
}
|
||||
// Storage: Scheduler Agenda (r:2 w:2)
|
||||
fn on_initialize_periodic(s: u32, ) -> Weight {
|
||||
(17_074_000 as Weight)
|
||||
// Standard Error: 16_000
|
||||
.saturating_add((11_982_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(s as Weight)))
|
||||
.saturating_add(T::DbWeight::get().writes(1 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(s as Weight)))
|
||||
}
|
||||
// Storage: Scheduler Agenda (r:1 w:1)
|
||||
// Storage: Scheduler Lookup (r:0 w:1)
|
||||
fn on_initialize_named(s: u32, ) -> Weight {
|
||||
(18_730_000 as Weight)
|
||||
// Standard Error: 10_000
|
||||
.saturating_add((9_909_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(1 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(s as Weight)))
|
||||
}
|
||||
// Storage: Scheduler Agenda (r:1 w:1)
|
||||
fn on_initialize(s: u32, ) -> Weight {
|
||||
(17_844_000 as Weight)
|
||||
// Standard Error: 9_000
|
||||
.saturating_add((7_719_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(1 as Weight))
|
||||
}
|
||||
// Storage: Scheduler Agenda (r:1 w:1)
|
||||
fn schedule(s: u32, ) -> Weight {
|
||||
(24_730_000 as Weight)
|
||||
(23_361_000 as Weight)
|
||||
// Standard Error: 1_000
|
||||
.saturating_add((77_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add((82_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(1 as Weight))
|
||||
}
|
||||
// Storage: Scheduler Agenda (r:1 w:1)
|
||||
// Storage: Scheduler Lookup (r:0 w:1)
|
||||
fn cancel(s: u32, ) -> Weight {
|
||||
(23_272_000 as Weight)
|
||||
// Standard Error: 4_000
|
||||
.saturating_add((1_261_000 as Weight).saturating_mul(s as Weight))
|
||||
(22_359_000 as Weight)
|
||||
// Standard Error: 2_000
|
||||
.saturating_add((1_219_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(2 as Weight))
|
||||
}
|
||||
// Storage: Scheduler Lookup (r:1 w:1)
|
||||
// Storage: Scheduler Agenda (r:1 w:1)
|
||||
fn schedule_named(s: u32, ) -> Weight {
|
||||
(30_971_000 as Weight)
|
||||
(28_499_000 as Weight)
|
||||
// Standard Error: 1_000
|
||||
.saturating_add((96_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add((98_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(2 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(2 as Weight))
|
||||
}
|
||||
// Storage: Scheduler Lookup (r:1 w:1)
|
||||
// Storage: Scheduler Agenda (r:1 w:1)
|
||||
fn cancel_named(s: u32, ) -> Weight {
|
||||
(25_778_000 as Weight)
|
||||
// Standard Error: 4_000
|
||||
.saturating_add((1_270_000 as Weight).saturating_mul(s as Weight))
|
||||
(24_995_000 as Weight)
|
||||
// Standard Error: 2_000
|
||||
.saturating_add((1_223_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(2 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(2 as Weight))
|
||||
}
|
||||
@@ -93,38 +213,149 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
||||
|
||||
// For backwards compatibility and tests
|
||||
impl WeightInfo for () {
|
||||
// Storage: Scheduler Agenda (r:2 w:2)
|
||||
// Storage: Preimage PreimageFor (r:1 w:1)
|
||||
// Storage: Preimage StatusFor (r:1 w:1)
|
||||
// Storage: Scheduler Lookup (r:0 w:1)
|
||||
fn on_initialize_periodic_named_resolved(s: u32, ) -> Weight {
|
||||
(8_183_000 as Weight)
|
||||
// Standard Error: 36_000
|
||||
.saturating_add((34_670_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads((3 as Weight).saturating_mul(s as Weight)))
|
||||
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes((4 as Weight).saturating_mul(s as Weight)))
|
||||
}
|
||||
// Storage: Scheduler Agenda (r:1 w:1)
|
||||
// Storage: Preimage PreimageFor (r:1 w:1)
|
||||
// Storage: Preimage StatusFor (r:1 w:1)
|
||||
// Storage: Scheduler Lookup (r:0 w:1)
|
||||
fn on_initialize_named_resolved(s: u32, ) -> Weight {
|
||||
(11_520_000 as Weight)
|
||||
// Standard Error: 30_000
|
||||
.saturating_add((26_386_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads((2 as Weight).saturating_mul(s as Weight)))
|
||||
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes((3 as Weight).saturating_mul(s as Weight)))
|
||||
}
|
||||
// Storage: Scheduler Agenda (r:2 w:2)
|
||||
// Storage: Preimage PreimageFor (r:1 w:1)
|
||||
// Storage: Preimage StatusFor (r:1 w:1)
|
||||
fn on_initialize_periodic_resolved(s: u32, ) -> Weight {
|
||||
(8_222_000 as Weight)
|
||||
// Standard Error: 33_000
|
||||
.saturating_add((28_925_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads((3 as Weight).saturating_mul(s as Weight)))
|
||||
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes((3 as Weight).saturating_mul(s as Weight)))
|
||||
}
|
||||
// Storage: Scheduler Agenda (r:1 w:1)
|
||||
// Storage: Preimage PreimageFor (r:1 w:1)
|
||||
// Storage: Preimage StatusFor (r:1 w:1)
|
||||
fn on_initialize_resolved(s: u32, ) -> Weight {
|
||||
(11_610_000 as Weight)
|
||||
// Standard Error: 26_000
|
||||
.saturating_add((23_857_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads((2 as Weight).saturating_mul(s as Weight)))
|
||||
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes((2 as Weight).saturating_mul(s as Weight)))
|
||||
}
|
||||
// Storage: Scheduler Agenda (r:2 w:2)
|
||||
// Storage: Preimage PreimageFor (r:1 w:0)
|
||||
// Storage: Scheduler Lookup (r:0 w:1)
|
||||
fn on_initialize_named_aborted(s: u32, ) -> Weight {
|
||||
(11_067_000 as Weight)
|
||||
// Standard Error: 15_000
|
||||
.saturating_add((11_728_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads(2 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(s as Weight)))
|
||||
.saturating_add(RocksDbWeight::get().writes(2 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(s as Weight)))
|
||||
}
|
||||
// Storage: Scheduler Agenda (r:2 w:2)
|
||||
// Storage: Preimage PreimageFor (r:1 w:0)
|
||||
fn on_initialize_aborted(s: u32, ) -> Weight {
|
||||
(13_045_000 as Weight)
|
||||
// Standard Error: 5_000
|
||||
.saturating_add((6_378_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads(2 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(s as Weight)))
|
||||
.saturating_add(RocksDbWeight::get().writes(2 as Weight))
|
||||
}
|
||||
// Storage: Scheduler Agenda (r:2 w:2)
|
||||
// Storage: Scheduler Lookup (r:0 w:1)
|
||||
fn on_initialize_periodic_named(s: u32, ) -> Weight {
|
||||
(13_496_000 as Weight)
|
||||
// Standard Error: 27_000
|
||||
.saturating_add((17_932_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(s as Weight)))
|
||||
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes((2 as Weight).saturating_mul(s as Weight)))
|
||||
}
|
||||
// Storage: Scheduler Agenda (r:2 w:2)
|
||||
fn on_initialize_periodic(s: u32, ) -> Weight {
|
||||
(17_074_000 as Weight)
|
||||
// Standard Error: 16_000
|
||||
.saturating_add((11_982_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(s as Weight)))
|
||||
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(s as Weight)))
|
||||
}
|
||||
// Storage: Scheduler Agenda (r:1 w:1)
|
||||
// Storage: Scheduler Lookup (r:0 w:1)
|
||||
fn on_initialize_named(s: u32, ) -> Weight {
|
||||
(18_730_000 as Weight)
|
||||
// Standard Error: 10_000
|
||||
.saturating_add((9_909_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(s as Weight)))
|
||||
}
|
||||
// Storage: Scheduler Agenda (r:1 w:1)
|
||||
fn on_initialize(s: u32, ) -> Weight {
|
||||
(17_844_000 as Weight)
|
||||
// Standard Error: 9_000
|
||||
.saturating_add((7_719_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
|
||||
}
|
||||
// Storage: Scheduler Agenda (r:1 w:1)
|
||||
fn schedule(s: u32, ) -> Weight {
|
||||
(24_730_000 as Weight)
|
||||
(23_361_000 as Weight)
|
||||
// Standard Error: 1_000
|
||||
.saturating_add((77_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add((82_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
|
||||
}
|
||||
// Storage: Scheduler Agenda (r:1 w:1)
|
||||
// Storage: Scheduler Lookup (r:0 w:1)
|
||||
fn cancel(s: u32, ) -> Weight {
|
||||
(23_272_000 as Weight)
|
||||
// Standard Error: 4_000
|
||||
.saturating_add((1_261_000 as Weight).saturating_mul(s as Weight))
|
||||
(22_359_000 as Weight)
|
||||
// Standard Error: 2_000
|
||||
.saturating_add((1_219_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes(2 as Weight))
|
||||
}
|
||||
// Storage: Scheduler Lookup (r:1 w:1)
|
||||
// Storage: Scheduler Agenda (r:1 w:1)
|
||||
fn schedule_named(s: u32, ) -> Weight {
|
||||
(30_971_000 as Weight)
|
||||
(28_499_000 as Weight)
|
||||
// Standard Error: 1_000
|
||||
.saturating_add((96_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add((98_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads(2 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes(2 as Weight))
|
||||
}
|
||||
// Storage: Scheduler Lookup (r:1 w:1)
|
||||
// Storage: Scheduler Agenda (r:1 w:1)
|
||||
fn cancel_named(s: u32, ) -> Weight {
|
||||
(25_778_000 as Weight)
|
||||
// Standard Error: 4_000
|
||||
.saturating_add((1_270_000 as Weight).saturating_mul(s as Weight))
|
||||
(24_995_000 as Weight)
|
||||
// Standard Error: 2_000
|
||||
.saturating_add((1_223_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads(2 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes(2 as Weight))
|
||||
}
|
||||
|
||||
@@ -53,8 +53,8 @@ pub use misc::{
|
||||
Backing, ConstBool, ConstI128, ConstI16, ConstI32, ConstI64, ConstI8, ConstU128, ConstU16,
|
||||
ConstU32, ConstU64, ConstU8, EnsureInherentsAreFirst, EqualPrivilegeOnly, EstimateCallFee,
|
||||
ExecuteBlock, ExtrinsicCall, Get, GetBacking, GetDefault, HandleLifetime, IsSubType, IsType,
|
||||
Len, OffchainWorker, OnKilledAccount, OnNewAccount, PrivilegeCmp, SameOrOther, Time, TryDrop,
|
||||
UnixTime, WrapperKeepOpaque, WrapperOpaque,
|
||||
Len, OffchainWorker, OnKilledAccount, OnNewAccount, PreimageProvider, PreimageRecipient,
|
||||
PrivilegeCmp, SameOrOther, Time, TryDrop, UnixTime, WrapperKeepOpaque, WrapperOpaque,
|
||||
};
|
||||
|
||||
mod stored_map;
|
||||
|
||||
@@ -94,6 +94,12 @@ pub trait TryDrop: Sized {
|
||||
fn try_drop(self) -> Result<(), Self>;
|
||||
}
|
||||
|
||||
impl TryDrop for () {
|
||||
fn try_drop(self) -> Result<(), Self> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Return type used when we need to return one of two items, each of the opposite direction or
|
||||
/// sign, with one (`Same`) being of the same type as the `self` or primary argument of the function
|
||||
/// that returned it.
|
||||
@@ -577,6 +583,65 @@ impl<T: TypeInfo + 'static> TypeInfo for WrapperKeepOpaque<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A interface for looking up preimages from their hash on chain.
|
||||
pub trait PreimageProvider<Hash> {
|
||||
/// Returns whether a preimage exists for a given hash.
|
||||
///
|
||||
/// A value of `true` implies that `get_preimage` is `Some`.
|
||||
fn have_preimage(hash: &Hash) -> bool;
|
||||
|
||||
/// Returns the preimage for a given hash.
|
||||
fn get_preimage(hash: &Hash) -> Option<Vec<u8>>;
|
||||
|
||||
/// Returns whether a preimage request exists for a given hash.
|
||||
fn preimage_requested(hash: &Hash) -> bool;
|
||||
|
||||
/// Request that someone report a preimage. Providers use this to optimise the economics for
|
||||
/// preimage reporting.
|
||||
fn request_preimage(hash: &Hash);
|
||||
|
||||
/// Cancel a previous preimage request.
|
||||
fn unrequest_preimage(hash: &Hash);
|
||||
}
|
||||
|
||||
impl<Hash> PreimageProvider<Hash> for () {
|
||||
fn have_preimage(_: &Hash) -> bool {
|
||||
false
|
||||
}
|
||||
fn get_preimage(_: &Hash) -> Option<Vec<u8>> {
|
||||
None
|
||||
}
|
||||
fn preimage_requested(_: &Hash) -> bool {
|
||||
false
|
||||
}
|
||||
fn request_preimage(_: &Hash) {}
|
||||
fn unrequest_preimage(_: &Hash) {}
|
||||
}
|
||||
|
||||
/// A interface for managing preimages to hashes on chain.
|
||||
///
|
||||
/// Note that this API does not assume any underlying user is calling, and thus
|
||||
/// does not handle any preimage ownership or fees. Other system level logic that
|
||||
/// uses this API should implement that on their own side.
|
||||
pub trait PreimageRecipient<Hash>: PreimageProvider<Hash> {
|
||||
/// Maximum size of a preimage.
|
||||
type MaxSize: Get<u32>;
|
||||
|
||||
/// Store the bytes of a preimage on chain.
|
||||
fn note_preimage(bytes: crate::BoundedVec<u8, Self::MaxSize>);
|
||||
|
||||
/// Clear a previously noted preimage. This is infallible and should be treated more like a
|
||||
/// hint - if it was not previously noted or if it is now requested, then this will not do
|
||||
/// anything.
|
||||
fn unnote_preimage(hash: &Hash);
|
||||
}
|
||||
|
||||
impl<Hash> PreimageRecipient<Hash> for () {
|
||||
type MaxSize = ();
|
||||
fn note_preimage(_: crate::BoundedVec<u8, Self::MaxSize>) {}
|
||||
fn unnote_preimage(_: &Hash) {}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
@@ -17,10 +17,10 @@
|
||||
|
||||
//! Traits and associated utilities for scheduling dispatchables in FRAME.
|
||||
|
||||
use codec::{Codec, Decode, Encode, EncodeLike};
|
||||
use codec::{Codec, Decode, Encode, EncodeLike, MaxEncodedLen};
|
||||
use scale_info::TypeInfo;
|
||||
use sp_runtime::{DispatchError, RuntimeDebug};
|
||||
use sp_std::{fmt::Debug, prelude::*};
|
||||
use sp_std::{fmt::Debug, prelude::*, result::Result};
|
||||
|
||||
/// Information relating to the period of a scheduled task. First item is the length of the
|
||||
/// period and the second is the number of times it should be executed in total before the task
|
||||
@@ -49,86 +49,323 @@ pub const HARD_DEADLINE: Priority = 63;
|
||||
/// The lowest priority. Most stuff should be around here.
|
||||
pub const LOWEST_PRIORITY: Priority = 255;
|
||||
|
||||
/// A type that can be used as a scheduler.
|
||||
pub trait Anon<BlockNumber, Call, Origin> {
|
||||
/// An address which can be used for removing a scheduled task.
|
||||
type Address: Codec + Clone + Eq + EncodeLike + Debug;
|
||||
|
||||
/// Schedule a dispatch to happen at the beginning of some block in the future.
|
||||
///
|
||||
/// This is not named.
|
||||
fn schedule(
|
||||
when: DispatchTime<BlockNumber>,
|
||||
maybe_periodic: Option<Period<BlockNumber>>,
|
||||
priority: Priority,
|
||||
origin: Origin,
|
||||
call: Call,
|
||||
) -> Result<Self::Address, DispatchError>;
|
||||
|
||||
/// Cancel a scheduled task. If periodic, then it will cancel all further instances of that,
|
||||
/// also.
|
||||
///
|
||||
/// Will return an error if the `address` is invalid.
|
||||
///
|
||||
/// NOTE: This guaranteed to work only *before* the point that it is due to be executed.
|
||||
/// If it ends up being delayed beyond the point of execution, then it cannot be cancelled.
|
||||
///
|
||||
/// NOTE2: This will not work to cancel periodic tasks after their initial execution. For
|
||||
/// that, you must name the task explicitly using the `Named` trait.
|
||||
fn cancel(address: Self::Address) -> Result<(), ()>;
|
||||
|
||||
/// Reschedule a task. For one-off tasks, this dispatch is guaranteed to succeed
|
||||
/// only if it is executed *before* the currently scheduled block. For periodic tasks,
|
||||
/// this dispatch is guaranteed to succeed only before the *initial* execution; for
|
||||
/// others, use `reschedule_named`.
|
||||
///
|
||||
/// Will return an error if the `address` is invalid.
|
||||
fn reschedule(
|
||||
address: Self::Address,
|
||||
when: DispatchTime<BlockNumber>,
|
||||
) -> Result<Self::Address, DispatchError>;
|
||||
|
||||
/// Return the next dispatch time for a given task.
|
||||
///
|
||||
/// Will return an error if the `address` is invalid.
|
||||
fn next_dispatch_time(address: Self::Address) -> Result<BlockNumber, ()>;
|
||||
/// Type representing an encodable value or the hash of the encoding of such a value.
|
||||
#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]
|
||||
pub enum MaybeHashed<T, Hash> {
|
||||
/// The value itself.
|
||||
Value(T),
|
||||
/// The hash of the encoded value which this value represents.
|
||||
Hash(Hash),
|
||||
}
|
||||
|
||||
/// A type that can be used as a scheduler.
|
||||
pub trait Named<BlockNumber, Call, Origin> {
|
||||
/// An address which can be used for removing a scheduled task.
|
||||
type Address: Codec + Clone + Eq + EncodeLike + sp_std::fmt::Debug;
|
||||
|
||||
/// Schedule a dispatch to happen at the beginning of some block in the future.
|
||||
///
|
||||
/// - `id`: The identity of the task. This must be unique and will return an error if not.
|
||||
fn schedule_named(
|
||||
id: Vec<u8>,
|
||||
when: DispatchTime<BlockNumber>,
|
||||
maybe_periodic: Option<Period<BlockNumber>>,
|
||||
priority: Priority,
|
||||
origin: Origin,
|
||||
call: Call,
|
||||
) -> Result<Self::Address, ()>;
|
||||
|
||||
/// Cancel a scheduled, named task. If periodic, then it will cancel all further instances
|
||||
/// of that, also.
|
||||
///
|
||||
/// Will return an error if the `id` is invalid.
|
||||
///
|
||||
/// NOTE: This guaranteed to work only *before* the point that it is due to be executed.
|
||||
/// If it ends up being delayed beyond the point of execution, then it cannot be cancelled.
|
||||
fn cancel_named(id: Vec<u8>) -> Result<(), ()>;
|
||||
|
||||
/// Reschedule a task. For one-off tasks, this dispatch is guaranteed to succeed
|
||||
/// only if it is executed *before* the currently scheduled block.
|
||||
fn reschedule_named(
|
||||
id: Vec<u8>,
|
||||
when: DispatchTime<BlockNumber>,
|
||||
) -> Result<Self::Address, DispatchError>;
|
||||
|
||||
/// Return the next dispatch time for a given task.
|
||||
///
|
||||
/// Will return an error if the `id` is invalid.
|
||||
fn next_dispatch_time(id: Vec<u8>) -> Result<BlockNumber, ()>;
|
||||
impl<T, H> From<T> for MaybeHashed<T, H> {
|
||||
fn from(t: T) -> Self {
|
||||
MaybeHashed::Value(t)
|
||||
}
|
||||
}
|
||||
|
||||
/// Error type for `MaybeHashed::lookup`.
|
||||
#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]
|
||||
pub enum LookupError {
|
||||
/// A call of this hash was not known.
|
||||
Unknown,
|
||||
/// The preimage for this hash was known but could not be decoded into a `Call`.
|
||||
BadFormat,
|
||||
}
|
||||
|
||||
impl<T: Decode, H> MaybeHashed<T, H> {
|
||||
pub fn as_value(&self) -> Option<&T> {
|
||||
match &self {
|
||||
Self::Value(c) => Some(c),
|
||||
Self::Hash(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_hash(&self) -> Option<&H> {
|
||||
match &self {
|
||||
Self::Value(_) => None,
|
||||
Self::Hash(h) => Some(h),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ensure_requested<P: PreimageProvider<H>>(&self) {
|
||||
match &self {
|
||||
Self::Value(_) => (),
|
||||
Self::Hash(hash) => P::request_preimage(hash),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ensure_unrequested<P: PreimageProvider<H>>(&self) {
|
||||
match &self {
|
||||
Self::Value(_) => (),
|
||||
Self::Hash(hash) => P::unrequest_preimage(hash),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolved<P: PreimageProvider<H>>(self) -> (Self, Option<H>) {
|
||||
match self {
|
||||
Self::Value(c) => (Self::Value(c), None),
|
||||
Self::Hash(h) => {
|
||||
let data = match P::get_preimage(&h) {
|
||||
Some(p) => p,
|
||||
None => return (Self::Hash(h), None),
|
||||
};
|
||||
match T::decode(&mut &data[..]) {
|
||||
Ok(c) => (Self::Value(c), Some(h)),
|
||||
Err(_) => (Self::Hash(h), None),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod v1 {
|
||||
use super::*;
|
||||
|
||||
/// A type that can be used as a scheduler.
|
||||
pub trait Anon<BlockNumber, Call, Origin> {
|
||||
/// An address which can be used for removing a scheduled task.
|
||||
type Address: Codec + Clone + Eq + EncodeLike + Debug;
|
||||
|
||||
/// Schedule a dispatch to happen at the beginning of some block in the future.
|
||||
///
|
||||
/// This is not named.
|
||||
fn schedule(
|
||||
when: DispatchTime<BlockNumber>,
|
||||
maybe_periodic: Option<Period<BlockNumber>>,
|
||||
priority: Priority,
|
||||
origin: Origin,
|
||||
call: Call,
|
||||
) -> Result<Self::Address, DispatchError>;
|
||||
|
||||
/// Cancel a scheduled task. If periodic, then it will cancel all further instances of that,
|
||||
/// also.
|
||||
///
|
||||
/// Will return an error if the `address` is invalid.
|
||||
///
|
||||
/// NOTE: This guaranteed to work only *before* the point that it is due to be executed.
|
||||
/// If it ends up being delayed beyond the point of execution, then it cannot be cancelled.
|
||||
///
|
||||
/// NOTE2: This will not work to cancel periodic tasks after their initial execution. For
|
||||
/// that, you must name the task explicitly using the `Named` trait.
|
||||
fn cancel(address: Self::Address) -> Result<(), ()>;
|
||||
|
||||
/// Reschedule a task. For one-off tasks, this dispatch is guaranteed to succeed
|
||||
/// only if it is executed *before* the currently scheduled block. For periodic tasks,
|
||||
/// this dispatch is guaranteed to succeed only before the *initial* execution; for
|
||||
/// others, use `reschedule_named`.
|
||||
///
|
||||
/// Will return an error if the `address` is invalid.
|
||||
fn reschedule(
|
||||
address: Self::Address,
|
||||
when: DispatchTime<BlockNumber>,
|
||||
) -> Result<Self::Address, DispatchError>;
|
||||
|
||||
/// Return the next dispatch time for a given task.
|
||||
///
|
||||
/// Will return an error if the `address` is invalid.
|
||||
fn next_dispatch_time(address: Self::Address) -> Result<BlockNumber, ()>;
|
||||
}
|
||||
|
||||
/// A type that can be used as a scheduler.
|
||||
pub trait Named<BlockNumber, Call, Origin> {
|
||||
/// An address which can be used for removing a scheduled task.
|
||||
type Address: Codec + Clone + Eq + EncodeLike + sp_std::fmt::Debug;
|
||||
|
||||
/// Schedule a dispatch to happen at the beginning of some block in the future.
|
||||
///
|
||||
/// - `id`: The identity of the task. This must be unique and will return an error if not.
|
||||
fn schedule_named(
|
||||
id: Vec<u8>,
|
||||
when: DispatchTime<BlockNumber>,
|
||||
maybe_periodic: Option<Period<BlockNumber>>,
|
||||
priority: Priority,
|
||||
origin: Origin,
|
||||
call: Call,
|
||||
) -> Result<Self::Address, ()>;
|
||||
|
||||
/// Cancel a scheduled, named task. If periodic, then it will cancel all further instances
|
||||
/// of that, also.
|
||||
///
|
||||
/// Will return an error if the `id` is invalid.
|
||||
///
|
||||
/// NOTE: This guaranteed to work only *before* the point that it is due to be executed.
|
||||
/// If it ends up being delayed beyond the point of execution, then it cannot be cancelled.
|
||||
fn cancel_named(id: Vec<u8>) -> Result<(), ()>;
|
||||
|
||||
/// Reschedule a task. For one-off tasks, this dispatch is guaranteed to succeed
|
||||
/// only if it is executed *before* the currently scheduled block.
|
||||
fn reschedule_named(
|
||||
id: Vec<u8>,
|
||||
when: DispatchTime<BlockNumber>,
|
||||
) -> Result<Self::Address, DispatchError>;
|
||||
|
||||
/// Return the next dispatch time for a given task.
|
||||
///
|
||||
/// Will return an error if the `id` is invalid.
|
||||
fn next_dispatch_time(id: Vec<u8>) -> Result<BlockNumber, ()>;
|
||||
}
|
||||
|
||||
impl<T, BlockNumber, Call, Origin> Anon<BlockNumber, Call, Origin> for T
|
||||
where
|
||||
T: v2::Anon<BlockNumber, Call, Origin>,
|
||||
{
|
||||
type Address = T::Address;
|
||||
|
||||
fn schedule(
|
||||
when: DispatchTime<BlockNumber>,
|
||||
maybe_periodic: Option<Period<BlockNumber>>,
|
||||
priority: Priority,
|
||||
origin: Origin,
|
||||
call: Call,
|
||||
) -> Result<Self::Address, DispatchError> {
|
||||
let c = MaybeHashed::<Call, T::Hash>::Value(call);
|
||||
T::schedule(when, maybe_periodic, priority, origin, c)
|
||||
}
|
||||
|
||||
fn cancel(address: Self::Address) -> Result<(), ()> {
|
||||
T::cancel(address)
|
||||
}
|
||||
|
||||
fn reschedule(
|
||||
address: Self::Address,
|
||||
when: DispatchTime<BlockNumber>,
|
||||
) -> Result<Self::Address, DispatchError> {
|
||||
T::reschedule(address, when)
|
||||
}
|
||||
|
||||
fn next_dispatch_time(address: Self::Address) -> Result<BlockNumber, ()> {
|
||||
T::next_dispatch_time(address)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, BlockNumber, Call, Origin> Named<BlockNumber, Call, Origin> for T
|
||||
where
|
||||
T: v2::Named<BlockNumber, Call, Origin>,
|
||||
{
|
||||
type Address = T::Address;
|
||||
|
||||
fn schedule_named(
|
||||
id: Vec<u8>,
|
||||
when: DispatchTime<BlockNumber>,
|
||||
maybe_periodic: Option<Period<BlockNumber>>,
|
||||
priority: Priority,
|
||||
origin: Origin,
|
||||
call: Call,
|
||||
) -> Result<Self::Address, ()> {
|
||||
let c = MaybeHashed::<Call, T::Hash>::Value(call);
|
||||
T::schedule_named(id, when, maybe_periodic, priority, origin, c)
|
||||
}
|
||||
|
||||
fn cancel_named(id: Vec<u8>) -> Result<(), ()> {
|
||||
T::cancel_named(id)
|
||||
}
|
||||
|
||||
fn reschedule_named(
|
||||
id: Vec<u8>,
|
||||
when: DispatchTime<BlockNumber>,
|
||||
) -> Result<Self::Address, DispatchError> {
|
||||
T::reschedule_named(id, when)
|
||||
}
|
||||
|
||||
fn next_dispatch_time(id: Vec<u8>) -> Result<BlockNumber, ()> {
|
||||
T::next_dispatch_time(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod v2 {
|
||||
use super::*;
|
||||
|
||||
/// A type that can be used as a scheduler.
|
||||
pub trait Anon<BlockNumber, Call, Origin> {
|
||||
/// An address which can be used for removing a scheduled task.
|
||||
type Address: Codec + Clone + Eq + EncodeLike + Debug;
|
||||
/// A means of expressing a call by the hash of its encoded data.
|
||||
type Hash;
|
||||
|
||||
/// Schedule a dispatch to happen at the beginning of some block in the future.
|
||||
///
|
||||
/// This is not named.
|
||||
fn schedule(
|
||||
when: DispatchTime<BlockNumber>,
|
||||
maybe_periodic: Option<Period<BlockNumber>>,
|
||||
priority: Priority,
|
||||
origin: Origin,
|
||||
call: MaybeHashed<Call, Self::Hash>,
|
||||
) -> Result<Self::Address, DispatchError>;
|
||||
|
||||
/// Cancel a scheduled task. If periodic, then it will cancel all further instances of that,
|
||||
/// also.
|
||||
///
|
||||
/// Will return an error if the `address` is invalid.
|
||||
///
|
||||
/// NOTE: This guaranteed to work only *before* the point that it is due to be executed.
|
||||
/// If it ends up being delayed beyond the point of execution, then it cannot be cancelled.
|
||||
///
|
||||
/// NOTE2: This will not work to cancel periodic tasks after their initial execution. For
|
||||
/// that, you must name the task explicitly using the `Named` trait.
|
||||
fn cancel(address: Self::Address) -> Result<(), ()>;
|
||||
|
||||
/// Reschedule a task. For one-off tasks, this dispatch is guaranteed to succeed
|
||||
/// only if it is executed *before* the currently scheduled block. For periodic tasks,
|
||||
/// this dispatch is guaranteed to succeed only before the *initial* execution; for
|
||||
/// others, use `reschedule_named`.
|
||||
///
|
||||
/// Will return an error if the `address` is invalid.
|
||||
fn reschedule(
|
||||
address: Self::Address,
|
||||
when: DispatchTime<BlockNumber>,
|
||||
) -> Result<Self::Address, DispatchError>;
|
||||
|
||||
/// Return the next dispatch time for a given task.
|
||||
///
|
||||
/// Will return an error if the `address` is invalid.
|
||||
fn next_dispatch_time(address: Self::Address) -> Result<BlockNumber, ()>;
|
||||
}
|
||||
|
||||
/// A type that can be used as a scheduler.
|
||||
pub trait Named<BlockNumber, Call, Origin> {
|
||||
/// An address which can be used for removing a scheduled task.
|
||||
type Address: Codec + Clone + Eq + EncodeLike + sp_std::fmt::Debug;
|
||||
/// A means of expressing a call by the hash of its encoded data.
|
||||
type Hash;
|
||||
|
||||
/// Schedule a dispatch to happen at the beginning of some block in the future.
|
||||
///
|
||||
/// - `id`: The identity of the task. This must be unique and will return an error if not.
|
||||
fn schedule_named(
|
||||
id: Vec<u8>,
|
||||
when: DispatchTime<BlockNumber>,
|
||||
maybe_periodic: Option<Period<BlockNumber>>,
|
||||
priority: Priority,
|
||||
origin: Origin,
|
||||
call: MaybeHashed<Call, Self::Hash>,
|
||||
) -> Result<Self::Address, ()>;
|
||||
|
||||
/// Cancel a scheduled, named task. If periodic, then it will cancel all further instances
|
||||
/// of that, also.
|
||||
///
|
||||
/// Will return an error if the `id` is invalid.
|
||||
///
|
||||
/// NOTE: This guaranteed to work only *before* the point that it is due to be executed.
|
||||
/// If it ends up being delayed beyond the point of execution, then it cannot be cancelled.
|
||||
fn cancel_named(id: Vec<u8>) -> Result<(), ()>;
|
||||
|
||||
/// Reschedule a task. For one-off tasks, this dispatch is guaranteed to succeed
|
||||
/// only if it is executed *before* the currently scheduled block.
|
||||
fn reschedule_named(
|
||||
id: Vec<u8>,
|
||||
when: DispatchTime<BlockNumber>,
|
||||
) -> Result<Self::Address, DispatchError>;
|
||||
|
||||
/// Return the next dispatch time for a given task.
|
||||
///
|
||||
/// Will return an error if the `id` is invalid.
|
||||
fn next_dispatch_time(id: Vec<u8>) -> Result<BlockNumber, ()>;
|
||||
}
|
||||
}
|
||||
|
||||
pub use v1::*;
|
||||
|
||||
use super::PreimageProvider;
|
||||
|
||||
@@ -199,3 +199,91 @@ pub trait Currency<AccountId> {
|
||||
balance: Self::Balance,
|
||||
) -> SignedImbalance<Self::Balance, Self::PositiveImbalance>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<AccountId> Currency<AccountId> for () {
|
||||
type Balance = u32;
|
||||
type PositiveImbalance = ();
|
||||
type NegativeImbalance = ();
|
||||
fn total_balance(_: &AccountId) -> Self::Balance {
|
||||
0
|
||||
}
|
||||
fn can_slash(_: &AccountId, _: Self::Balance) -> bool {
|
||||
true
|
||||
}
|
||||
fn total_issuance() -> Self::Balance {
|
||||
0
|
||||
}
|
||||
fn minimum_balance() -> Self::Balance {
|
||||
0
|
||||
}
|
||||
fn burn(_: Self::Balance) -> Self::PositiveImbalance {
|
||||
()
|
||||
}
|
||||
fn issue(_: Self::Balance) -> Self::NegativeImbalance {
|
||||
()
|
||||
}
|
||||
fn pair(_: Self::Balance) -> (Self::PositiveImbalance, Self::NegativeImbalance) {
|
||||
((), ())
|
||||
}
|
||||
fn free_balance(_: &AccountId) -> Self::Balance {
|
||||
0
|
||||
}
|
||||
fn ensure_can_withdraw(
|
||||
_: &AccountId,
|
||||
_: Self::Balance,
|
||||
_: WithdrawReasons,
|
||||
_: Self::Balance,
|
||||
) -> DispatchResult {
|
||||
Ok(())
|
||||
}
|
||||
fn transfer(
|
||||
_: &AccountId,
|
||||
_: &AccountId,
|
||||
_: Self::Balance,
|
||||
_: ExistenceRequirement,
|
||||
) -> DispatchResult {
|
||||
Ok(())
|
||||
}
|
||||
fn slash(_: &AccountId, _: Self::Balance) -> (Self::NegativeImbalance, Self::Balance) {
|
||||
((), 0)
|
||||
}
|
||||
fn deposit_into_existing(
|
||||
_: &AccountId,
|
||||
_: Self::Balance,
|
||||
) -> Result<Self::PositiveImbalance, DispatchError> {
|
||||
Ok(())
|
||||
}
|
||||
fn resolve_into_existing(
|
||||
_: &AccountId,
|
||||
_: Self::NegativeImbalance,
|
||||
) -> Result<(), Self::NegativeImbalance> {
|
||||
Ok(())
|
||||
}
|
||||
fn deposit_creating(_: &AccountId, _: Self::Balance) -> Self::PositiveImbalance {
|
||||
()
|
||||
}
|
||||
fn resolve_creating(_: &AccountId, _: Self::NegativeImbalance) {}
|
||||
fn withdraw(
|
||||
_: &AccountId,
|
||||
_: Self::Balance,
|
||||
_: WithdrawReasons,
|
||||
_: ExistenceRequirement,
|
||||
) -> Result<Self::NegativeImbalance, DispatchError> {
|
||||
Ok(())
|
||||
}
|
||||
fn settle(
|
||||
_: &AccountId,
|
||||
_: Self::PositiveImbalance,
|
||||
_: WithdrawReasons,
|
||||
_: ExistenceRequirement,
|
||||
) -> Result<(), Self::PositiveImbalance> {
|
||||
Ok(())
|
||||
}
|
||||
fn make_free_balance_be(
|
||||
_: &AccountId,
|
||||
_: Self::Balance,
|
||||
) -> SignedImbalance<Self::Balance, Self::PositiveImbalance> {
|
||||
SignedImbalance::Positive(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +81,33 @@ pub trait ReservableCurrency<AccountId>: Currency<AccountId> {
|
||||
) -> Result<Self::Balance, DispatchError>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<AccountId> ReservableCurrency<AccountId> for () {
|
||||
fn can_reserve(_: &AccountId, _: Self::Balance) -> bool {
|
||||
true
|
||||
}
|
||||
fn slash_reserved(_: &AccountId, _: Self::Balance) -> (Self::NegativeImbalance, Self::Balance) {
|
||||
((), 0)
|
||||
}
|
||||
fn reserved_balance(_: &AccountId) -> Self::Balance {
|
||||
0
|
||||
}
|
||||
fn reserve(_: &AccountId, _: Self::Balance) -> DispatchResult {
|
||||
Ok(())
|
||||
}
|
||||
fn unreserve(_: &AccountId, _: Self::Balance) -> Self::Balance {
|
||||
0
|
||||
}
|
||||
fn repatriate_reserved(
|
||||
_: &AccountId,
|
||||
_: &AccountId,
|
||||
_: Self::Balance,
|
||||
_: BalanceStatus,
|
||||
) -> Result<Self::Balance, DispatchError> {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait NamedReservableCurrency<AccountId>: ReservableCurrency<AccountId> {
|
||||
/// An identifier for a reserve. Used for disambiguating different reserves so that
|
||||
/// they can be individually replaced or removed.
|
||||
|
||||
@@ -177,3 +177,55 @@ pub trait Imbalance<Balance>: Sized + TryDrop + Default {
|
||||
/// The raw value of self.
|
||||
fn peek(&self) -> Balance;
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<Balance: Default> Imbalance<Balance> for () {
|
||||
type Opposite = ();
|
||||
fn zero() -> Self {
|
||||
()
|
||||
}
|
||||
fn drop_zero(self) -> Result<(), Self> {
|
||||
Ok(())
|
||||
}
|
||||
fn split(self, _: Balance) -> (Self, Self) {
|
||||
((), ())
|
||||
}
|
||||
fn ration(self, _: u32, _: u32) -> (Self, Self)
|
||||
where
|
||||
Balance: From<u32> + Saturating + Div<Output = Balance>,
|
||||
{
|
||||
((), ())
|
||||
}
|
||||
fn split_merge(self, _: Balance, _: (Self, Self)) -> (Self, Self) {
|
||||
((), ())
|
||||
}
|
||||
fn ration_merge(self, _: u32, _: u32, _: (Self, Self)) -> (Self, Self)
|
||||
where
|
||||
Balance: From<u32> + Saturating + Div<Output = Balance>,
|
||||
{
|
||||
((), ())
|
||||
}
|
||||
fn split_merge_into(self, _: Balance, _: &mut (Self, Self)) {}
|
||||
fn ration_merge_into(self, _: u32, _: u32, _: &mut (Self, Self))
|
||||
where
|
||||
Balance: From<u32> + Saturating + Div<Output = Balance>,
|
||||
{
|
||||
}
|
||||
fn merge(self, _: Self) -> Self {
|
||||
()
|
||||
}
|
||||
fn merge_into(self, _: &mut Self) {}
|
||||
fn maybe_merge(self, _: Option<Self>) -> Self {
|
||||
()
|
||||
}
|
||||
fn subsume(&mut self, _: Self) {}
|
||||
fn maybe_subsume(&mut self, _: Option<Self>) {
|
||||
()
|
||||
}
|
||||
fn offset(self, _: Self::Opposite) -> SameOrOther<Self, Self::Opposite> {
|
||||
SameOrOther::None
|
||||
}
|
||||
fn peek(&self) -> Balance {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,7 +344,7 @@ pub mod pallet {
|
||||
/// # </weight>
|
||||
#[pallet::weight(T::SystemWeightInfo::remark(_remark.len() as u32))]
|
||||
pub fn remark(origin: OriginFor<T>, _remark: Vec<u8>) -> DispatchResultWithPostInfo {
|
||||
ensure_signed(origin)?;
|
||||
ensure_signed_or_root(origin)?;
|
||||
Ok(().into())
|
||||
}
|
||||
|
||||
@@ -872,6 +872,22 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensure that the origin `o` represents either a signed extrinsic (i.e. transaction) or the root.
|
||||
/// Returns `Ok` with the account that signed the extrinsic, `None` if it was root, or an `Err`
|
||||
/// otherwise.
|
||||
pub fn ensure_signed_or_root<OuterOrigin, AccountId>(
|
||||
o: OuterOrigin,
|
||||
) -> Result<Option<AccountId>, BadOrigin>
|
||||
where
|
||||
OuterOrigin: Into<Result<RawOrigin<AccountId>, OuterOrigin>>,
|
||||
{
|
||||
match o.into() {
|
||||
Ok(RawOrigin::Root) => Ok(None),
|
||||
Ok(RawOrigin::Signed(t)) => Ok(Some(t)),
|
||||
_ => Err(BadOrigin),
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensure that the origin `o` represents the root. Returns `Ok` or an `Err` otherwise.
|
||||
pub fn ensure_root<OuterOrigin, AccountId>(o: OuterOrigin) -> Result<(), BadOrigin>
|
||||
where
|
||||
|
||||
Reference in New Issue
Block a user