--- a/Cargo.lock +++ b/Cargo.lock @@ -5385,7 +5385,6 @@ "pallet-treasury", "pallet-unique", "pallet-unique-scheduler", - "pallet-unique-scheduler-v2", "pallet-xcm", "parachain-info", "parity-scale-codec 3.2.1", @@ -6709,7 +6708,7 @@ dependencies = [ "frame-support", "frame-system", - "pallet-unique-scheduler-v2", + "pallet-unique-scheduler", "parity-scale-codec 3.2.1", "scale-info", ] @@ -6851,24 +6850,6 @@ "sp-std", "substrate-test-utils", "up-sponsorship", -] - -[[package]] -name = "pallet-unique-scheduler-v2" -version = "0.1.0" -dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-system", - "log", - "pallet-preimage", - "parity-scale-codec 3.2.1", - "scale-info", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", - "substrate-test-utils", ] [[package]] --- a/client/rpc/src/lib.rs +++ b/client/rpc/src/lib.rs @@ -545,13 +545,16 @@ keys: Option> ) -> Vec, unique_api); - pass_method!(token_data( - collection: CollectionId, - token_id: TokenId, + pass_method!( + token_data( + collection: CollectionId, + token_id: TokenId, - #[map(|keys| string_keys_to_bytes_keys(keys))] - keys: Option>, - ) -> TokenData, unique_api); + #[map(|keys| string_keys_to_bytes_keys(keys))] + keys: Option>, + ) -> TokenData, unique_api; + changed_in 3, token_data_before_version_3(collection, token_id, string_keys_to_bytes_keys(keys)) => |value| value.into() + ); pass_method!(adminlist(collection: CollectionId) -> Vec, unique_api); pass_method!(allowlist(collection: CollectionId) -> Vec, unique_api); --- /dev/null +++ b/kb/how-not-to-break-rpc.md @@ -0,0 +1,169 @@ +# How not to break RPC + +Let's discuss how to avoid the breaking of RPC with an example of how the `colection_by_id` RPC method was broken. + +The `collection_by_id` was broken due to the change of the result type `RpcCollection`. +The new version of the `RpcCollection` was incompatible with the old one due to addition of the `flags` field: + +```rust +// The new version of the `RpcCollection` +pub struct RpcCollection { + /// Collection owner account. + pub owner: AccountId, + /// Collection mode. + pub mode: CollectionMode, + /// Collection name. + pub name: Vec, + /// Collection description. + pub description: Vec, + /// Token prefix. + pub token_prefix: Vec, + /// The state of sponsorship of the collection. + pub sponsorship: SponsorshipState, + /// Collection limits. + pub limits: CollectionLimits, + /// Collection permissions. + pub permissions: CollectionPermissions, + /// Token property permissions. + pub token_property_permissions: Vec, + /// Collection properties. + pub properties: Vec, + /// Is collection read only. + pub read_only: bool, + + /// Extra collection flags + pub flags: RpcCollectionFlags, // <-- THIS IS A NEW FIELD! +} +``` + +### Where exactly was RPC broken? + +To answer this question, we need to describe the process of handling an RPC call. + +1. A user calls an RPC method. +2. The node sees what method with what arguments should be executed. +3. Since the code of each RPC method is located inside the runtime, the node does the following: + - The node encodes the RPC arguments into the [SCALE format](https://docs.substrate.io/reference/scale-codec/), and then it will call the corresponding method of the runtime API with the encoded arguments. + - The runtime executes the RPC logic and then returns the SCALE-encoded result. + - The node receives the result from the runtime and then decodes it. **It is the place where RPC could break!** + +Point #3 describes a process implemented inside the [`pass_method`](https://github.com/UniqueNetwork/unique-chain/blob/1c7179877b5fb1eacf86c5ecf607317d11999675/client/rpc/src/lib.rs#L435-L472) macro. + +Using the `pass_method` macro the node maps each RPC method onto the corresponding runtime API method. + +See how [the node's RPC is implemented](https://github.com/UniqueNetwork/unique-chain/blob/1c7179877b5fb1eacf86c5ecf607317d11999675/client/rpc/src/lib.rs#L493-L569) and how [the runtime API is declared](https://github.com/UniqueNetwork/unique-chain/blob/1c7179877b5fb1eacf86c5ecf607317d11999675/primitives/rpc/src/lib.rs#L32-L129). + +### How can the node use the old runtime API? + +As you can see from the previous section -- RPC breaks if the runtime API data format is incompatible with the node's RPC data format. + +When the node is working with an old runtime and exposes the new version of RPC that contains some methods with a changed signature, the node should call only the old versions of these methods to avoid RPC failure. + +The node should do the following to get an old runtime API method to run correctly: +* The node should convert all the RPC arguments into the old runtime API format. +* It should convert the result from the runtime to the new data format (it is the only action needed in the case of `collection_by_id`). + +The `pass_method` macro can call the old runtime API methods. +For instance, the correct implementation of the `collection_by_id` RPC method looks like this: +```rust +pass_method!( + /* line 1 */ collection_by_id(collection: CollectionId) -> Option>, unique_api; + /* line 2 */ changed_in 3, collection_by_id_before_version_3(collection) => |value| value.map(|coll| coll.into()) +); +``` + +The first line describes the newest RPC signature. + +The second line tells us what should be called in the case if we're dealing with an old runtime API. +* `collection_by_id_before_version_3` -- the name of the corresponding runtime API method with an old signature. +* `(collection)` -- what arguments should the node pass to the old method. In the case of `collection_by_id`, we pass the arguments as is since there were no changes to the arguments' types. +* `=> |value| value.map(|coll| coll.into())` -- describes how to transform the return value from the old runtime API data format into the new RPC data format. + +### Runtime API backward compatibility support + +Methods like `collection_by_id_before_version_3` doesn't appear automatically. + +When changing the runtime API methods' signatures, we need to: +* Specify the number of the new version of the runtime API. +* Specify the old versions of the changed methods. + +See the documentation of the `decl_runtime_apis` macro: [runtime api trait versioning](https://docs.rs/sp-api/latest/sp_api/macro.decl_runtime_apis.html#runtime-api-trait-versioning). + +### How to easily implement the converting from the old structure into the new ones + +To describe structures that can have some fields changing over different versions, we use the `#[struct_versioning::versioned]` attribute. + +Let's take a look at the `RpcCollection` declaration. + +```rust +/// Collection parameters, used in RPC calls (see [`Collection`] for the storage version). +#[struct_versioning::versioned(version = 2, upper)] +#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)] +#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] +pub struct RpcCollection { + /// Collection owner account. + pub owner: AccountId, + + /// Collection mode. + pub mode: CollectionMode, + + /// Collection name. + pub name: Vec, + + /// Collection description. + pub description: Vec, + + /// Token prefix. + pub token_prefix: Vec, + + /// The state of sponsorship of the collection. + pub sponsorship: SponsorshipState, + + /// Collection limits. + pub limits: CollectionLimits, + + /// Collection permissions. + pub permissions: CollectionPermissions, + + /// Token property permissions. + pub token_property_permissions: Vec, + + /// Collection properties. + pub properties: Vec, + + /// Is collection read only. + pub read_only: bool, + + /// Extra collection flags + #[version(2.., upper(RpcCollectionFlags {foreign: false, erc721metadata: false}))] + pub flags: RpcCollectionFlags, +} +``` + +The `#[struct_versioning::versioned]` will create 2 types for us: the `RpcCollectionVersion1` (the old version) and the `RpcCollection` (the new version). + +This attribute automatically implements the `impl From for RpcCollection`. + +The attribute understands how to map the old fields to new ones with the help of the `#[version(...)]` field attribute, which should be placed right before the field in question. + +There were no field `flags` in the `RpcCollectionVersion1` structure. The `#[version(2.., upper())]` tells the attribute to assign the `flags` field to `` in the new version of the `RpcCollection` structure. + +Given that we have the `From` trait implemented for the new version of the `RpcCollection`, we can use `.into()` to convert the old version to the new one as we did in the `pass_method` macro above. + +Here is the description of the `struct_versioning` attribute: +``` +Generate versioned variants of a struct + + `#[versioned(version = 1[, first_version = 1][, upper][, versions])]` + - *version* - current version of a struct + - *first_version* - allows to skip generation of structs, which predates first supported version + - *upper* - generate From impls, which converts old version of structs to new + - *versions* - generate enum, which contains all possible versions of struct + + Each field may have version attribute + `#[version([1]..[2][, upper(old)])]` + - *1* - version, on which this field is appeared + - *2* - version, in which this field was removed + (i.e if set to 2, this field was exist on version 1, and no longer exist on version 2) + - *upper* - code, which should be executed to transform old value to new/create new value +``` --- a/pallets/scheduler-v2/Cargo.toml +++ /dev/null @@ -1,48 +0,0 @@ -[package] -name = "pallet-unique-scheduler-v2" -version = "0.1.0" -authors = ["Unique Network "] -edition = "2021" -license = "GPLv3" -homepage = "https://unique.network" -repository = "https://github.com/UniqueNetwork/unique-chain" -description = "Unique Scheduler pallet" -readme = "README.md" - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } -log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } -frame-benchmarking = { default-features = false, optional = true, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.30" } -frame-support = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.30" } -frame-system = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.30" } -sp-io = { version = "6.0.0", default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.30" } -sp-runtime = { version = "6.0.0", default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.30" } -sp-std = { version = "4.0.0", default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.30" } -sp-core = { version = "6.0.0", default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.30" } - -[dev-dependencies] -pallet-preimage = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.30" } -sp-core = { version = "6.0.0", default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.30" } -substrate-test-utils = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.30" } - -[features] -default = ["std"] -runtime-benchmarks = [ - "frame-benchmarking", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", -] -std = [ - "codec/std", - "frame-benchmarking?/std", - "frame-support/std", - "frame-system/std", - "log/std", - "scale-info/std", - "sp-io/std", - "sp-runtime/std", - "sp-std/std", - "sp-core/std", -] -try-runtime = ["frame-support/try-runtime"] --- a/pallets/scheduler-v2/src/benchmarking.rs +++ /dev/null @@ -1,349 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -// Original license: -// This file is part of Substrate. - -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Scheduler pallet benchmarking. - -use super::*; -use frame_benchmarking::{account, benchmarks}; -use frame_support::{ - ensure, - traits::{schedule::Priority, PreimageRecipient}, -}; -use frame_system::RawOrigin; -use sp_std::{prelude::*, vec}; -use sp_io::hashing::blake2_256; - -use crate::{Pallet as Scheduler, ScheduledCall, EncodedCall}; -use frame_system::Call as SystemCall; - -const SEED: u32 = 0; - -const BLOCK_NUMBER: u32 = 2; - -type SystemOrigin = ::Origin; - -/// Add `n` 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(when: T::BlockNumber, n: u32) -> Result<(), &'static str> { - let t = DispatchTime::At(when); - let origin: ::PalletsOrigin = frame_system::RawOrigin::Root.into(); - for i in 0..n { - let call = make_call::(None); - let period = Some(((i + 100).into(), 100)); - let name = u32_to_name(i); - Scheduler::::do_schedule_named(name, t, period, 0, origin.clone(), call)?; - } - ensure!( - Agenda::::get(when).len() == n as usize, - "didn't fill schedule" - ); - Ok(()) -} - -fn u32_to_name(i: u32) -> TaskName { - i.using_encoded(blake2_256) -} - -fn make_task( - periodic: bool, - named: bool, - signed: bool, - maybe_lookup_len: Option, - priority: Priority, -) -> ScheduledOf { - let call = make_call::(maybe_lookup_len); - let maybe_periodic = match periodic { - true => Some((100u32.into(), 100)), - false => None, - }; - let maybe_id = match named { - true => Some(u32_to_name(0)), - false => None, - }; - let origin = make_origin::(signed); - Scheduled { - maybe_id, - priority, - call, - maybe_periodic, - origin, - _phantom: PhantomData, - } -} - -fn bounded(len: u32) -> Option> { - let call = <::Call>::from(SystemCall::remark { - remark: vec![0; len as usize], - }); - ScheduledCall::new(call).ok() -} - -fn make_call(maybe_lookup_len: Option) -> ScheduledCall { - let bound = EncodedCall::bound() as u32; - let mut len = match maybe_lookup_len { - Some(len) => { - len.min(>::MaxSize::get() - 2) - .max(bound) - 3 - } - None => bound.saturating_sub(4), - }; - - loop { - let c = match bounded::(len) { - Some(x) => x, - None => { - len -= 1; - continue; - } - }; - if c.lookup_needed() == maybe_lookup_len.is_some() { - break c; - } - if maybe_lookup_len.is_some() { - len += 1; - } else { - if len > 0 { - len -= 1; - } else { - break c; - } - } - } -} - -fn make_origin(signed: bool) -> ::PalletsOrigin { - match signed { - true => frame_system::RawOrigin::Signed(account("origin", 0, SEED)).into(), - false => frame_system::RawOrigin::Root.into(), - } -} - -fn dummy_counter() -> WeightCounter { - WeightCounter { - used: Weight::zero(), - limit: Weight::MAX, - } -} - -benchmarks! { - // `service_agendas` when no work is done. - service_agendas_base { - let now = T::BlockNumber::from(BLOCK_NUMBER); - IncompleteSince::::put(now - One::one()); - }: { - Scheduler::::service_agendas(&mut dummy_counter(), now, 0); - } verify { - assert_eq!(IncompleteSince::::get(), Some(now - One::one())); - } - - // `service_agenda` when no work is done. - service_agenda_base { - let now = BLOCK_NUMBER.into(); - let s in 0 .. T::MaxScheduledPerBlock::get(); - fill_schedule::(now, s)?; - let mut executed = 0; - }: { - Scheduler::::service_agenda(&mut dummy_counter(), &mut executed, now, now, 0); - } verify { - assert_eq!(executed, 0); - } - - // `service_task` when the task is a non-periodic, non-named, non-fetched call which is not - // dispatched (e.g. due to being overweight). - service_task_base { - let now = BLOCK_NUMBER.into(); - let task = make_task::(false, false, false, None, 0); - // prevent any tasks from actually being executed as we only want the surrounding weight. - let mut counter = WeightCounter { used: Weight::zero(), limit: Weight::zero() }; - }: { - let result = Scheduler::::service_task(&mut counter, now, now, 0, true, task); - } verify { - //assert_eq!(result, Ok(())); - } - - // `service_task` when the task is a non-periodic, non-named, fetched call (with a known - // preimage length) and which is not dispatched (e.g. due to being overweight). - service_task_fetched { - let s in (EncodedCall::bound() as u32) .. (>::MaxSize::get()); - let now = BLOCK_NUMBER.into(); - let task = make_task::(false, false, false, Some(s), 0); - // prevent any tasks from actually being executed as we only want the surrounding weight. - let mut counter = WeightCounter { used: Weight::zero(), limit: Weight::zero() }; - }: { - let result = Scheduler::::service_task(&mut counter, now, now, 0, true, task); - } verify { - } - - // `service_task` when the task is a non-periodic, named, non-fetched call which is not - // dispatched (e.g. due to being overweight). - service_task_named { - let now = BLOCK_NUMBER.into(); - let task = make_task::(false, true, false, None, 0); - // prevent any tasks from actually being executed as we only want the surrounding weight. - let mut counter = WeightCounter { used: Weight::zero(), limit: Weight::zero() }; - }: { - let result = Scheduler::::service_task(&mut counter, now, now, 0, true, task); - } verify { - } - - // `service_task` when the task is a periodic, non-named, non-fetched call which is not - // dispatched (e.g. due to being overweight). - service_task_periodic { - let now = BLOCK_NUMBER.into(); - let task = make_task::(true, false, false, None, 0); - // prevent any tasks from actually being executed as we only want the surrounding weight. - let mut counter = WeightCounter { used: Weight::zero(), limit: Weight::zero() }; - }: { - let result = Scheduler::::service_task(&mut counter, now, now, 0, true, task); - } verify { - } - - // `execute_dispatch` when the origin is `Signed`, not counting the dispatable's weight. - execute_dispatch_signed { - let mut counter = WeightCounter { used: Weight::zero(), limit: Weight::MAX }; - let origin = make_origin::(true); - let call = T::Preimages::realize(&make_call::(None)).unwrap().0; - }: { - assert!(Scheduler::::execute_dispatch(&mut counter, origin, call).is_ok()); - } - verify { - } - - // `execute_dispatch` when the origin is not `Signed`, not counting the dispatable's weight. - execute_dispatch_unsigned { - let mut counter = WeightCounter { used: Weight::zero(), limit: Weight::MAX }; - let origin = make_origin::(false); - let call = T::Preimages::realize(&make_call::(None)).unwrap().0; - }: { - assert!(Scheduler::::execute_dispatch(&mut counter, origin, call).is_ok()); - } - verify { - } - - schedule { - let s in 0 .. (T::MaxScheduledPerBlock::get() - 1); - let when = BLOCK_NUMBER.into(); - let periodic = Some((T::BlockNumber::one(), 100)); - let priority = Some(0); - // Essentially a no-op call. - let call = Box::new(SystemCall::set_storage { items: vec![] }.into()); - - fill_schedule::(when, s)?; - }: _(RawOrigin::Root, when, periodic, priority, call) - verify { - ensure!( - Agenda::::get(when).len() == (s + 1) as usize, - "didn't add to schedule" - ); - } - - cancel { - let s in 1 .. T::MaxScheduledPerBlock::get(); - let when = BLOCK_NUMBER.into(); - - fill_schedule::(when, s)?; - assert_eq!(Agenda::::get(when).len(), s as usize); - let schedule_origin = T::ScheduleOrigin::successful_origin(); - }: _>(schedule_origin, when, 0) - verify { - ensure!( - Lookup::::get(u32_to_name(0)).is_none(), - "didn't remove from lookup" - ); - // Removed schedule is NONE - ensure!( - Agenda::::get(when)[0].is_none(), - "didn't remove from schedule" - ); - } - - schedule_named { - let s in 0 .. (T::MaxScheduledPerBlock::get() - 1); - let id = u32_to_name(s); - let when = BLOCK_NUMBER.into(); - let periodic = Some((T::BlockNumber::one(), 100)); - let priority = Some(0); - // Essentially a no-op call. - let call = Box::new(SystemCall::set_storage { items: vec![] }.into()); - - fill_schedule::(when, s)?; - }: _(RawOrigin::Root, id, when, periodic, priority, call) - verify { - ensure!( - Agenda::::get(when).len() == (s + 1) as usize, - "didn't add to schedule" - ); - } - - cancel_named { - let s in 1 .. T::MaxScheduledPerBlock::get(); - let when = BLOCK_NUMBER.into(); - - fill_schedule::(when, s)?; - }: _(RawOrigin::Root, u32_to_name(0)) - verify { - ensure!( - Lookup::::get(u32_to_name(0)).is_none(), - "didn't remove from lookup" - ); - // Removed schedule is NONE - ensure!( - Agenda::::get(when)[0].is_none(), - "didn't remove from schedule" - ); - } - - change_named_priority { - let origin: RawOrigin = frame_system::RawOrigin::Root; - let s in 1 .. T::MaxScheduledPerBlock::get(); - let when = BLOCK_NUMBER.into(); - let idx = s - 1; - let id = u32_to_name(idx); - let priority = 42; - fill_schedule::(when, s)?; - }: _(origin, id, priority) - verify { - ensure!( - Agenda::::get(when)[idx as usize].clone().unwrap().priority == priority, - "didn't change the priority" - ); - } - - impl_benchmark_test_suite!(Scheduler, crate::mock::new_test_ext(), crate::mock::Test); -} --- a/pallets/scheduler-v2/src/lib.rs +++ /dev/null @@ -1,1102 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -// Original license: -// This file is part of Substrate. - -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! # Scheduler -//! A Pallet for scheduling dispatches. -//! -//! - [`Config`] -//! - [`Call`] -//! - [`Pallet`] -//! -//! ## Overview -//! -//! This Pallet exposes capabilities for scheduling dispatches to occur at a -//! specified block number or at a specified period. These scheduled dispatches -//! may be named or anonymous and may be canceled. -//! -//! **NOTE:** The scheduled calls will be dispatched with the default filter -//! for the origin: namely `frame_system::Config::BaseCallFilter` for all origin -//! except root which will get no filter. And not the filter contained in origin -//! use to call `fn schedule`. -//! -//! If a call is scheduled using proxy or whatever mecanism which adds filter, -//! then those filter will not be used when dispatching the schedule call. -//! -//! ## Interface -//! -//! ### Dispatchable Functions -//! -//! * `schedule` - schedule a dispatch, which may be periodic, to occur at a specified block and -//! with a specified priority. -//! * `cancel` - cancel a scheduled dispatch, specified by block number and index. -//! * `schedule_named` - augments the `schedule` interface with an additional `Vec` parameter -//! that can be used for identification. -//! * `cancel_named` - the named complement to the cancel function. - -// Ensure we're `no_std` when compiling for Wasm. -#![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 codec::{Codec, Decode, Encode, MaxEncodedLen}; -use frame_support::{ - dispatch::{ - DispatchError, DispatchResult, Dispatchable, GetDispatchInfo, Parameter, PostDispatchInfo, - }, - traits::{ - schedule::{self, DispatchTime, LOWEST_PRIORITY}, - EnsureOrigin, Get, IsType, OriginTrait, PrivilegeCmp, StorageVersion, PreimageRecipient, - ConstU32, UnfilteredDispatchable, - }, - weights::Weight, - unsigned::TransactionValidityError, -}; - -use frame_system::{self as system}; -use scale_info::TypeInfo; -use sp_runtime::{ - traits::{BadOrigin, One, Saturating, Zero, Hash}, - BoundedVec, RuntimeDebug, DispatchErrorWithPostInfo, -}; -use sp_core::H160; -use sp_std::{borrow::Borrow, cmp::Ordering, marker::PhantomData, prelude::*}; -pub use weights::WeightInfo; - -pub use pallet::*; - -/// Just a simple index for naming period tasks. -pub type PeriodicIndex = u32; -/// The location of a scheduled task that can be used to remove it. -pub type TaskAddress = (BlockNumber, u32); - -pub type EncodedCall = BoundedVec>; - -#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] -#[scale_info(skip_type_params(T))] -pub enum ScheduledCall { - Inline(EncodedCall), - PreimageLookup { hash: T::Hash, unbounded_len: u32 }, -} - -impl ScheduledCall { - pub fn new(call: ::RuntimeCall) -> Result { - let encoded = call.encode(); - let len = encoded.len(); - - match EncodedCall::try_from(encoded.clone()) { - Ok(bounded) => Ok(Self::Inline(bounded)), - Err(_) => { - let hash = ::Hashing::hash_of(&encoded); - ::Preimages::note_preimage( - encoded - .try_into() - .map_err(|_| >::TooBigScheduledCall)?, - ); - - Ok(Self::PreimageLookup { - hash, - unbounded_len: len as u32, - }) - } - } - } - - /// The maximum length of the lookup that is needed to peek `Self`. - pub fn lookup_len(&self) -> Option { - match self { - Self::Inline(..) => None, - Self::PreimageLookup { unbounded_len, .. } => Some(*unbounded_len), - } - } - - /// Returns whether the image will require a lookup to be peeked. - pub fn lookup_needed(&self) -> bool { - match self { - Self::Inline(_) => false, - Self::PreimageLookup { .. } => true, - } - } - - fn decode(mut data: &[u8]) -> Result<::RuntimeCall, DispatchError> { - ::RuntimeCall::decode(&mut data) - .map_err(|_| >::ScheduledCallCorrupted.into()) - } -} - -pub trait SchedulerPreimages: PreimageRecipient { - fn drop(call: &ScheduledCall); - - fn peek( - call: &ScheduledCall, - ) -> Result<(::RuntimeCall, Option), DispatchError>; - - /// Convert the given scheduled `call` value back into its original instance. If successful, - /// `drop` any data backing it. This will not break the realisability of independently - /// created instances of `ScheduledCall` which happen to have identical data. - fn realize( - call: &ScheduledCall, - ) -> Result<(::RuntimeCall, Option), DispatchError>; -} - -impl> SchedulerPreimages for PP { - fn drop(call: &ScheduledCall) { - match call { - ScheduledCall::Inline(_) => {} - ScheduledCall::PreimageLookup { hash, .. } => Self::unrequest_preimage(hash), - } - } - - fn peek( - call: &ScheduledCall, - ) -> Result<(::RuntimeCall, Option), DispatchError> { - match call { - ScheduledCall::Inline(data) => Ok((ScheduledCall::::decode(data)?, None)), - ScheduledCall::PreimageLookup { - hash, - unbounded_len, - } => { - let (preimage, len) = Self::get_preimage(hash) - .ok_or(>::PreimageNotFound) - .map(|preimage| (preimage, *unbounded_len))?; - - Ok((ScheduledCall::::decode(preimage.as_slice())?, Some(len))) - } - } - } - - fn realize( - call: &ScheduledCall, - ) -> Result<(::RuntimeCall, Option), DispatchError> { - let r = Self::peek(call)?; - Self::drop(call); - Ok(r) - } -} - -pub enum ScheduledEnsureOriginSuccess { - Root, - Signed(AccountId), -} - -pub type TaskName = [u8; 32]; - -/// Information regarding an item to be executed in the future. -#[cfg_attr(any(feature = "std", test), derive(PartialEq, Eq))] -#[derive(Clone, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)] -pub struct Scheduled { - /// The unique identity for this task, if there is one. - maybe_id: Option, - - /// This task's priority. - priority: schedule::Priority, - - /// The call to be dispatched. - call: Call, - - /// If the call is periodic, then this points to the information concerning that. - maybe_periodic: Option>, - - /// The origin with which to dispatch the call. - origin: PalletsOrigin, - _phantom: PhantomData, -} - -pub type ScheduledOf = Scheduled< - TaskName, - ScheduledCall, - ::BlockNumber, - ::PalletsOrigin, - ::AccountId, ->; - -struct WeightCounter { - used: Weight, - limit: Weight, -} - -impl WeightCounter { - fn check_accrue(&mut self, w: Weight) -> bool { - let test = self.used.saturating_add(w); - if test.any_gt(self.limit) { - false - } else { - self.used = test; - true - } - } - - fn can_accrue(&mut self, w: Weight) -> bool { - self.used.saturating_add(w).all_lte(self.limit) - } -} - -pub(crate) trait MarginalWeightInfo: WeightInfo { - fn service_task(maybe_lookup_len: Option, named: bool, periodic: bool) -> Weight { - let base = Self::service_task_base(); - let mut total = match maybe_lookup_len { - None => base, - Some(l) => Self::service_task_fetched(l as u32), - }; - if named { - total.saturating_accrue(Self::service_task_named().saturating_sub(base)); - } - if periodic { - total.saturating_accrue(Self::service_task_periodic().saturating_sub(base)); - } - total - } -} - -impl MarginalWeightInfo for T {} - -#[frame_support::pallet] -pub mod pallet { - use super::*; - use frame_support::{dispatch::PostDispatchInfo, pallet_prelude::*}; - use system::pallet_prelude::*; - - /// The current storage version. - const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); - - #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] - #[pallet::storage_version(STORAGE_VERSION)] - pub struct Pallet(_); - - #[pallet::config] - pub trait Config: frame_system::Config { - type RuntimeEvent: From> + IsType<::RuntimeEvent>; - - /// The aggregated origin which the dispatch will take. - type RuntimeOrigin: OriginTrait - + From - + IsType<::RuntimeOrigin> - + Clone; - - /// The caller origin, overarching type of all pallets origins. - type PalletsOrigin: From> - + Codec - + Clone - + Eq - + TypeInfo - + MaxEncodedLen; - - /// The aggregated call type. - type RuntimeCall: Parameter - + Dispatchable< - RuntimeOrigin = ::RuntimeOrigin, - PostInfo = PostDispatchInfo, - > + UnfilteredDispatchable::RuntimeOrigin> - + GetDispatchInfo - + From>; - - /// The maximum weight that may be scheduled per block for any dispatchables. - #[pallet::constant] - type MaximumWeight: Get; - - /// Required origin to schedule or cancel calls. - type ScheduleOrigin: EnsureOrigin< - ::RuntimeOrigin, - Success = ScheduledEnsureOriginSuccess, - >; - - /// Compare the privileges of origins. - /// - /// This will be used when canceling a task, to ensure that the origin that tries - /// to cancel has greater or equal privileges as the origin that created the scheduled task. - /// - /// For simplicity the [`EqualPrivilegeOnly`](frame_support::traits::EqualPrivilegeOnly) can - /// be used. This will only check if two given origins are equal. - type OriginPrivilegeCmp: PrivilegeCmp; - - /// The maximum number of scheduled calls in the queue for a single block. - #[pallet::constant] - type MaxScheduledPerBlock: Get; - - /// Weight information for extrinsics in this pallet. - type WeightInfo: WeightInfo; - - /// The preimage provider with which we look up call hashes to get the call. - type Preimages: SchedulerPreimages; - - /// The helper type used for custom transaction fee logic. - type CallExecutor: DispatchCall; - - /// Required origin to set/change calls' priority. - type PrioritySetOrigin: EnsureOrigin<::RuntimeOrigin>; - } - - #[pallet::storage] - pub type IncompleteSince = StorageValue<_, T::BlockNumber>; - - /// Items to be executed, indexed by the block number that they should be executed on. - #[pallet::storage] - pub type Agenda = StorageMap< - _, - Twox64Concat, - T::BlockNumber, - BoundedVec>, T::MaxScheduledPerBlock>, - ValueQuery, - >; - - /// Lookup from a name to the block number and index of the task. - #[pallet::storage] - pub(crate) type Lookup = - StorageMap<_, Twox64Concat, TaskName, TaskAddress>; - - /// Events type. - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event { - /// Scheduled some task. - Scheduled { when: T::BlockNumber, index: u32 }, - /// Canceled some task. - Canceled { when: T::BlockNumber, index: u32 }, - /// Dispatched some task. - Dispatched { - task: TaskAddress, - id: Option<[u8; 32]>, - result: DispatchResult, - }, - /// Scheduled task's priority has changed - PriorityChanged { - when: T::BlockNumber, - index: u32, - priority: schedule::Priority, - }, - /// The call for the provided hash was not found so the task has been aborted. - CallUnavailable { - task: TaskAddress, - id: Option<[u8; 32]>, - }, - /// The given task was unable to be renewed since the agenda is full at that block. - PeriodicFailed { - task: TaskAddress, - id: Option<[u8; 32]>, - }, - /// The given task can never be executed since it is overweight. - PermanentlyOverweight { - task: TaskAddress, - id: Option<[u8; 32]>, - }, - } - - #[pallet::error] - pub enum Error { - /// Failed to schedule a call - FailedToSchedule, - /// There is no place for a new task in the agenda - AgendaIsExhausted, - /// Scheduled call is corrupted - ScheduledCallCorrupted, - /// Scheduled call preimage is not found - PreimageNotFound, - /// Scheduled call is too big - TooBigScheduledCall, - /// Cannot find the scheduled call. - NotFound, - /// Given target block number is in the past. - TargetBlockNumberInPast, - /// Attempt to use a non-named function on a named task. - Named, - } - - #[pallet::hooks] - impl Hooks> for Pallet { - /// Execute the scheduled calls - fn on_initialize(now: T::BlockNumber) -> Weight { - let mut weight_counter = WeightCounter { - used: Weight::zero(), - limit: T::MaximumWeight::get(), - }; - Self::service_agendas(&mut weight_counter, now, u32::max_value()); - weight_counter.used - } - } - - #[pallet::call] - impl Pallet { - /// Anonymously schedule a task. - #[pallet::weight(::WeightInfo::schedule(T::MaxScheduledPerBlock::get()))] - pub fn schedule( - origin: OriginFor, - when: T::BlockNumber, - maybe_periodic: Option>, - priority: Option, - call: Box<::RuntimeCall>, - ) -> DispatchResult { - T::ScheduleOrigin::ensure_origin(origin.clone())?; - - if priority.is_some() { - T::PrioritySetOrigin::ensure_origin(origin.clone())?; - } - - let origin = ::RuntimeOrigin::from(origin); - Self::do_schedule( - DispatchTime::At(when), - maybe_periodic, - priority.unwrap_or(LOWEST_PRIORITY), - origin.caller().clone(), - >::new(*call)?, - )?; - Ok(()) - } - - /// Cancel an anonymously scheduled task. - #[pallet::weight(::WeightInfo::cancel(T::MaxScheduledPerBlock::get()))] - pub fn cancel(origin: OriginFor, when: T::BlockNumber, index: u32) -> DispatchResult { - T::ScheduleOrigin::ensure_origin(origin.clone())?; - let origin = ::RuntimeOrigin::from(origin); - Self::do_cancel(Some(origin.caller().clone()), (when, index))?; - Ok(()) - } - - /// Schedule a named task. - #[pallet::weight(::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get()))] - pub fn schedule_named( - origin: OriginFor, - id: TaskName, - when: T::BlockNumber, - maybe_periodic: Option>, - priority: Option, - call: Box<::RuntimeCall>, - ) -> DispatchResult { - T::ScheduleOrigin::ensure_origin(origin.clone())?; - - if priority.is_some() { - T::PrioritySetOrigin::ensure_origin(origin.clone())?; - } - - let origin = ::RuntimeOrigin::from(origin); - Self::do_schedule_named( - id, - DispatchTime::At(when), - maybe_periodic, - priority.unwrap_or(LOWEST_PRIORITY), - origin.caller().clone(), - >::new(*call)?, - )?; - Ok(()) - } - - /// Cancel a named scheduled task. - #[pallet::weight(::WeightInfo::cancel_named(T::MaxScheduledPerBlock::get()))] - pub fn cancel_named(origin: OriginFor, id: TaskName) -> DispatchResult { - T::ScheduleOrigin::ensure_origin(origin.clone())?; - let origin = ::RuntimeOrigin::from(origin); - Self::do_cancel_named(Some(origin.caller().clone()), id)?; - Ok(()) - } - - /// Anonymously schedule a task after a delay. - /// - /// # - /// Same as [`schedule`]. - /// # - #[pallet::weight(::WeightInfo::schedule(T::MaxScheduledPerBlock::get()))] - pub fn schedule_after( - origin: OriginFor, - after: T::BlockNumber, - maybe_periodic: Option>, - priority: Option, - call: Box<::RuntimeCall>, - ) -> DispatchResult { - T::ScheduleOrigin::ensure_origin(origin.clone())?; - - if priority.is_some() { - T::PrioritySetOrigin::ensure_origin(origin.clone())?; - } - - let origin = ::RuntimeOrigin::from(origin); - Self::do_schedule( - DispatchTime::After(after), - maybe_periodic, - priority.unwrap_or(LOWEST_PRIORITY), - origin.caller().clone(), - >::new(*call)?, - )?; - Ok(()) - } - - /// Schedule a named task after a delay. - /// - /// # - /// Same as [`schedule_named`](Self::schedule_named). - /// # - #[pallet::weight(::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get()))] - pub fn schedule_named_after( - origin: OriginFor, - id: TaskName, - after: T::BlockNumber, - maybe_periodic: Option>, - priority: Option, - call: Box<::RuntimeCall>, - ) -> DispatchResult { - T::ScheduleOrigin::ensure_origin(origin.clone())?; - - if priority.is_some() { - T::PrioritySetOrigin::ensure_origin(origin.clone())?; - } - - let origin = ::RuntimeOrigin::from(origin); - Self::do_schedule_named( - id, - DispatchTime::After(after), - maybe_periodic, - priority.unwrap_or(LOWEST_PRIORITY), - origin.caller().clone(), - >::new(*call)?, - )?; - Ok(()) - } - - #[pallet::weight(::WeightInfo::change_named_priority(T::MaxScheduledPerBlock::get()))] - pub fn change_named_priority( - origin: OriginFor, - id: TaskName, - priority: schedule::Priority, - ) -> DispatchResult { - T::PrioritySetOrigin::ensure_origin(origin.clone())?; - let origin = ::RuntimeOrigin::from(origin); - Self::do_change_named_priority(origin.caller().clone(), id, priority) - } - } -} - -impl Pallet { - fn resolve_time(when: DispatchTime) -> Result { - let now = frame_system::Pallet::::block_number(); - - let when = match when { - DispatchTime::At(x) => x, - // The current block has already completed it's scheduled tasks, so - // Schedule the task at lest one block after this current block. - DispatchTime::After(x) => now.saturating_add(x).saturating_add(One::one()), - }; - - if when <= now { - return Err(Error::::TargetBlockNumberInPast.into()); - } - - Ok(when) - } - - fn place_task( - when: T::BlockNumber, - what: ScheduledOf, - ) -> Result, (DispatchError, ScheduledOf)> { - let maybe_name = what.maybe_id; - let index = Self::push_to_agenda(when, what)?; - let address = (when, index); - if let Some(name) = maybe_name { - Lookup::::insert(name, address) - } - Self::deposit_event(Event::Scheduled { - when: address.0, - index: address.1, - }); - Ok(address) - } - - fn push_to_agenda( - when: T::BlockNumber, - what: ScheduledOf, - ) -> Result)> { - let mut agenda = Agenda::::get(when); - let index = if (agenda.len() as u32) < T::MaxScheduledPerBlock::get() { - // will always succeed due to the above check. - let _ = agenda.try_push(Some(what)); - agenda.len() as u32 - 1 - } else { - if let Some(hole_index) = agenda.iter().position(|i| i.is_none()) { - agenda[hole_index] = Some(what); - hole_index as u32 - } else { - return Err((>::AgendaIsExhausted.into(), what)); - } - }; - Agenda::::insert(when, agenda); - Ok(index) - } - - fn do_schedule( - when: DispatchTime, - maybe_periodic: Option>, - priority: schedule::Priority, - origin: T::PalletsOrigin, - call: ScheduledCall, - ) -> Result, DispatchError> { - let when = Self::resolve_time(when)?; - - // sanitize maybe_periodic - let maybe_periodic = maybe_periodic - .filter(|p| p.1 > 1 && !p.0.is_zero()) - // Remove one from the number of repetitions since we will schedule one now. - .map(|(p, c)| (p, c - 1)); - let task = Scheduled { - maybe_id: None, - priority, - call, - maybe_periodic, - origin, - _phantom: PhantomData, - }; - Self::place_task(when, task).map_err(|x| x.0) - } - - fn do_cancel( - origin: Option, - (when, index): TaskAddress, - ) -> Result<(), DispatchError> { - let scheduled = Agenda::::try_mutate(when, |agenda| { - agenda.get_mut(index as usize).map_or( - Ok(None), - |s| -> Result>, DispatchError> { - if let (Some(ref o), Some(ref s)) = (origin, s.borrow()) { - if matches!( - T::OriginPrivilegeCmp::cmp_privilege(o, &s.origin), - Some(Ordering::Less) | None - ) { - return Err(BadOrigin.into()); - } - }; - Ok(s.take()) - }, - ) - })?; - if let Some(s) = scheduled { - T::Preimages::drop(&s.call); - - if let Some(id) = s.maybe_id { - Lookup::::remove(id); - } - Self::deposit_event(Event::Canceled { when, index }); - Ok(()) - } else { - return Err(Error::::NotFound.into()); - } - } - - fn do_schedule_named( - id: TaskName, - when: DispatchTime, - maybe_periodic: Option>, - priority: schedule::Priority, - origin: T::PalletsOrigin, - call: ScheduledCall, - ) -> Result, DispatchError> { - // ensure id it is unique - if Lookup::::contains_key(&id) { - return Err(Error::::FailedToSchedule.into()); - } - - let when = Self::resolve_time(when)?; - - // sanitize maybe_periodic - let maybe_periodic = maybe_periodic - .filter(|p| p.1 > 1 && !p.0.is_zero()) - // Remove one from the number of repetitions since we will schedule one now. - .map(|(p, c)| (p, c - 1)); - - let task = Scheduled { - maybe_id: Some(id), - priority, - call, - maybe_periodic, - origin, - _phantom: Default::default(), - }; - Self::place_task(when, task).map_err(|x| x.0) - } - - fn do_cancel_named(origin: Option, id: TaskName) -> DispatchResult { - Lookup::::try_mutate_exists(id, |lookup| -> DispatchResult { - if let Some((when, index)) = lookup.take() { - let i = index as usize; - Agenda::::try_mutate(when, |agenda| -> DispatchResult { - if let Some(s) = agenda.get_mut(i) { - if let (Some(ref o), Some(ref s)) = (origin, s.borrow()) { - if matches!( - T::OriginPrivilegeCmp::cmp_privilege(o, &s.origin), - Some(Ordering::Less) | None - ) { - return Err(BadOrigin.into()); - } - T::Preimages::drop(&s.call); - } - *s = None; - } - Ok(()) - })?; - Self::deposit_event(Event::Canceled { when, index }); - Ok(()) - } else { - return Err(Error::::NotFound.into()); - } - }) - } - - fn do_change_named_priority( - origin: T::PalletsOrigin, - id: TaskName, - priority: schedule::Priority, - ) -> DispatchResult { - match Lookup::::get(id) { - Some((when, index)) => { - let i = index as usize; - Agenda::::try_mutate(when, |agenda| { - if let Some(Some(s)) = agenda.get_mut(i) { - if matches!( - T::OriginPrivilegeCmp::cmp_privilege(&origin, &s.origin), - Some(Ordering::Less) | None - ) { - return Err(BadOrigin.into()); - } - - s.priority = priority; - Self::deposit_event(Event::PriorityChanged { - when, - index, - priority, - }); - } - Ok(()) - }) - } - None => Err(Error::::NotFound.into()), - } - } -} - -enum ServiceTaskError { - /// Could not be executed due to missing preimage. - Unavailable, - /// Could not be executed due to weight limitations. - Overweight, -} -use ServiceTaskError::*; - -/// A Scheduler-Runtime interface for finer payment handling. -pub trait DispatchCall { - /// Resolve the call dispatch, including any post-dispatch operations. - fn dispatch_call( - signer: Option, - function: ::RuntimeCall, - ) -> Result< - Result>, - TransactionValidityError, - >; -} - -impl Pallet { - /// Service up to `max` agendas queue starting from earliest incompletely executed agenda. - fn service_agendas(weight: &mut WeightCounter, now: T::BlockNumber, max: u32) { - if !weight.check_accrue(T::WeightInfo::service_agendas_base()) { - return; - } - - let mut incomplete_since = now + One::one(); - let mut when = IncompleteSince::::take().unwrap_or(now); - let mut executed = 0; - - let max_items = T::MaxScheduledPerBlock::get(); - let mut count_down = max; - let service_agenda_base_weight = T::WeightInfo::service_agenda_base(max_items); - while count_down > 0 && when <= now && weight.can_accrue(service_agenda_base_weight) { - if !Self::service_agenda(weight, &mut executed, now, when, u32::max_value()) { - incomplete_since = incomplete_since.min(when); - } - when.saturating_inc(); - count_down.saturating_dec(); - } - incomplete_since = incomplete_since.min(when); - if incomplete_since <= now { - IncompleteSince::::put(incomplete_since); - } - } - - /// Returns `true` if the agenda was fully completed, `false` if it should be revisited at a - /// later block. - fn service_agenda( - weight: &mut WeightCounter, - executed: &mut u32, - now: T::BlockNumber, - when: T::BlockNumber, - max: u32, - ) -> bool { - let mut agenda = Agenda::::get(when); - let mut ordered = agenda - .iter() - .enumerate() - .filter_map(|(index, maybe_item)| { - maybe_item - .as_ref() - .map(|item| (index as u32, item.priority)) - }) - .collect::>(); - ordered.sort_by_key(|k| k.1); - let within_limit = - weight.check_accrue(T::WeightInfo::service_agenda_base(ordered.len() as u32)); - debug_assert!( - within_limit, - "weight limit should have been checked in advance" - ); - - // Items which we know can be executed and have postponed for execution in a later block. - let mut postponed = (ordered.len() as u32).saturating_sub(max); - // Items which we don't know can ever be executed. - let mut dropped = 0; - - for (agenda_index, _) in ordered.into_iter().take(max as usize) { - let task = match agenda[agenda_index as usize].take() { - None => continue, - Some(t) => t, - }; - let base_weight = T::WeightInfo::service_task( - task.call.lookup_len().map(|x| x as usize), - task.maybe_id.is_some(), - task.maybe_periodic.is_some(), - ); - if !weight.can_accrue(base_weight) { - postponed += 1; - break; - } - let result = Self::service_task(weight, now, when, agenda_index, *executed == 0, task); - agenda[agenda_index as usize] = match result { - Err((Unavailable, slot)) => { - dropped += 1; - slot - } - Err((Overweight, slot)) => { - postponed += 1; - slot - } - Ok(()) => { - *executed += 1; - None - } - }; - } - if postponed > 0 || dropped > 0 { - Agenda::::insert(when, agenda); - } else { - Agenda::::remove(when); - } - postponed == 0 - } - - /// Service (i.e. execute) the given task, being careful not to overflow the `weight` counter. - /// - /// This involves: - /// - removing and potentially replacing the `Lookup` entry for the task. - /// - realizing the task's call which can include a preimage lookup. - /// - Rescheduling the task for execution in a later agenda if periodic. - fn service_task( - weight: &mut WeightCounter, - now: T::BlockNumber, - when: T::BlockNumber, - agenda_index: u32, - is_first: bool, - mut task: ScheduledOf, - ) -> Result<(), (ServiceTaskError, Option>)> { - let (call, lookup_len) = match T::Preimages::peek(&task.call) { - Ok(c) => c, - Err(_) => { - if let Some(ref id) = task.maybe_id { - Lookup::::remove(id); - } - - return Err((Unavailable, Some(task))); - } - }; - - weight.check_accrue(T::WeightInfo::service_task( - lookup_len.map(|x| x as usize), - task.maybe_id.is_some(), - task.maybe_periodic.is_some(), - )); - - match Self::execute_dispatch(weight, task.origin.clone(), call) { - Err(Unavailable) => { - debug_assert!(false, "Checked to exist with `peek`"); - - if let Some(ref id) = task.maybe_id { - Lookup::::remove(id); - } - - Self::deposit_event(Event::CallUnavailable { - task: (when, agenda_index), - id: task.maybe_id, - }); - Err((Unavailable, Some(task))) - } - Err(Overweight) if is_first && !Self::is_runtime_upgraded() => { - T::Preimages::drop(&task.call); - - if let Some(ref id) = task.maybe_id { - Lookup::::remove(id); - } - - Self::deposit_event(Event::PermanentlyOverweight { - task: (when, agenda_index), - id: task.maybe_id, - }); - Err((Unavailable, Some(task))) - } - Err(Overweight) => { - // Preserve Lookup -- the task will be postponed. - Err((Overweight, Some(task))) - } - Ok(result) => { - Self::deposit_event(Event::Dispatched { - task: (when, agenda_index), - id: task.maybe_id, - result, - }); - - let is_canceled = task - .maybe_id - .as_ref() - .map(|id| !Lookup::::contains_key(id)) - .unwrap_or(false); - - match &task.maybe_periodic { - &Some((period, count)) if !is_canceled => { - if count > 1 { - task.maybe_periodic = Some((period, count - 1)); - } else { - task.maybe_periodic = None; - } - let wake = now.saturating_add(period); - match Self::place_task(wake, task) { - Ok(_) => {} - Err((_, task)) => { - // TODO: Leave task in storage somewhere for it to be rescheduled - // manually. - T::Preimages::drop(&task.call); - Self::deposit_event(Event::PeriodicFailed { - task: (when, agenda_index), - id: task.maybe_id, - }); - } - } - } - _ => { - if let Some(ref id) = task.maybe_id { - Lookup::::remove(id); - } - - T::Preimages::drop(&task.call) - } - } - Ok(()) - } - } - } - - fn is_runtime_upgraded() -> bool { - let last = system::LastRuntimeUpgrade::::get(); - let current = T::Version::get(); - - last.map(|v| v.was_upgraded(¤t)).unwrap_or(true) - } - - /// Make a dispatch to the given `call` from the given `origin`, ensuring that the `weight` - /// counter does not exceed its limit and that it is counted accurately (e.g. accounted using - /// post info if available). - /// - /// NOTE: Only the weight for this function will be counted (origin lookup, dispatch and the - /// call itself). - fn execute_dispatch( - weight: &mut WeightCounter, - origin: T::PalletsOrigin, - call: ::RuntimeCall, - ) -> Result { - let dispatch_origin: ::RuntimeOrigin = origin.into(); - let base_weight = match dispatch_origin.clone().as_signed() { - Some(_) => T::WeightInfo::execute_dispatch_signed(), - _ => T::WeightInfo::execute_dispatch_unsigned(), - }; - let call_weight = call.get_dispatch_info().weight; - // We only allow a scheduled call if it cannot push the weight past the limit. - let max_weight = base_weight.saturating_add(call_weight); - - if !weight.can_accrue(max_weight) { - return Err(Overweight); - } - - // let scheduled_origin = - // <::Origin as From>::from(origin.clone()); - let ensured_origin = T::ScheduleOrigin::ensure_origin(dispatch_origin.into()); - - let r = match ensured_origin { - Ok(ScheduledEnsureOriginSuccess::Root) => { - Ok(call.dispatch_bypass_filter(frame_system::RawOrigin::Root.into())) - } - Ok(ScheduledEnsureOriginSuccess::Signed(sender)) => { - // Execute transaction via chain default pipeline - // That means dispatch will be processed like any user's extrinsic e.g. transaction fees will be taken - T::CallExecutor::dispatch_call(Some(sender), call.clone()) - } - Err(e) => Ok(Err(e.into())), - }; - - let (maybe_actual_call_weight, result) = match r { - Ok(result) => match result { - Ok(post_info) => (post_info.actual_weight, Ok(())), - Err(error_and_info) => ( - error_and_info.post_info.actual_weight, - Err(error_and_info.error), - ), - }, - Err(_) => { - log::error!( - target: "runtime::scheduler", - "Warning: Scheduler has failed to execute a post-dispatch transaction. \ - This block might have become invalid."); - (None, Err(DispatchError::CannotLookup)) - } - }; - let call_weight = maybe_actual_call_weight.unwrap_or(call_weight); - weight.check_accrue(base_weight); - weight.check_accrue(call_weight); - Ok(result) - } -} --- a/pallets/scheduler-v2/src/mock.rs +++ /dev/null @@ -1,285 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -// Original license: -// This file is part of Substrate. - -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! # Scheduler test environment. - -use super::*; - -use crate as scheduler; -use frame_support::{ - ord_parameter_types, parameter_types, - traits::{ConstU32, ConstU64, Contains, EqualPrivilegeOnly, OnFinalize, OnInitialize}, - weights::constants::RocksDbWeight, -}; -use frame_system::{EnsureRoot, RawOrigin}; -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::{OriginCaller, OriginTrait}; - use frame_support::{pallet_prelude::*, parameter_types}; - use frame_system::pallet_prelude::*; - - parameter_types! { - static Log: Vec<(OriginCaller, u32)> = Vec::new(); - } - pub fn log() -> Vec<(OriginCaller, u32)> { - Log::get().clone() - } - - #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] - pub struct Pallet(PhantomData); - - #[pallet::hooks] - impl Hooks> for Pallet {} - - #[pallet::config] - pub trait Config: frame_system::Config { - type RuntimeEvent: From> + IsType<::RuntimeEvent>; - } - - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event { - Logged(u32, Weight), - } - - #[pallet::call] - impl Pallet - where - ::RuntimeOrigin: OriginTrait, - { - #[pallet::weight(*weight)] - pub fn log(origin: OriginFor, i: u32, weight: Weight) -> DispatchResult { - Self::deposit_event(Event::Logged(i, weight)); - Log::mutate(|log| { - log.push((origin.caller().clone(), i)); - }); - Ok(()) - } - - #[pallet::weight(*weight)] - pub fn log_without_filter(origin: OriginFor, i: u32, weight: Weight) -> DispatchResult { - Self::deposit_event(Event::Logged(i, weight)); - Log::mutate(|log| { - log.push((origin.caller().clone(), i)); - }); - Ok(()) - } - } -} - -type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; -type Block = frame_system::mocking::MockBlock; - -frame_support::construct_runtime!( - pub enum Test where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic, - { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Logger: logger::{Pallet, Call, Event}, - Scheduler: scheduler::{Pallet, Call, Storage, Event}, - } -); - -// Scheduler must dispatch with root and no filter, this tests base filter is indeed not used. -pub struct BaseFilter; -impl Contains for BaseFilter { - fn contains(call: &RuntimeCall) -> bool { - !matches!(call, RuntimeCall::Logger(LoggerCall::log { .. })) - } -} - -parameter_types! { - pub BlockWeights: frame_system::limits::BlockWeights = - frame_system::limits::BlockWeights::simple_max( - Weight::from_ref_time(2_000_000_000_000) - // .set_proof_size(u64::MAX), - ); -} -impl system::Config for Test { - type BaseCallFilter = BaseFilter; - type BlockWeights = BlockWeights; - type BlockLength = (); - type DbWeight = RocksDbWeight; - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Index = u64; - type BlockNumber = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; - type Header = Header; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; -} -impl logger::Config for Test { - type RuntimeEvent = RuntimeEvent; -} -ord_parameter_types! { - pub const One: u64 = 1; -} - -pub struct TestWeightInfo; -impl WeightInfo for TestWeightInfo { - fn service_agendas_base() -> Weight { - Weight::from_ref_time(0b0000_0001) - } - fn service_agenda_base(i: u32) -> Weight { - Weight::from_ref_time((i << 8) as u64 + 0b0000_0010) - } - fn service_task_base() -> Weight { - Weight::from_ref_time(0b0000_0100) - } - fn service_task_periodic() -> Weight { - Weight::from_ref_time(0b0000_1100) - } - fn service_task_named() -> Weight { - Weight::from_ref_time(0b0001_0100) - } - fn service_task_fetched(s: u32) -> Weight { - Weight::from_ref_time((s << 8) as u64 + 0b0010_0100) - } - fn execute_dispatch_signed() -> Weight { - Weight::from_ref_time(0b0100_0000) - } - fn execute_dispatch_unsigned() -> Weight { - Weight::from_ref_time(0b1000_0000) - } - fn schedule(_s: u32) -> Weight { - Weight::from_ref_time(50) - } - fn cancel(_s: u32) -> Weight { - Weight::from_ref_time(50) - } - fn schedule_named(_s: u32) -> Weight { - Weight::from_ref_time(50) - } - fn cancel_named(_s: u32) -> Weight { - Weight::from_ref_time(50) - } - fn change_named_priority(_s: u32) -> Weight { - Weight::from_ref_time(50) - } -} -parameter_types! { - pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * - BlockWeights::get().max_block; -} - -pub struct EnsureSignedOneOrRoot; -impl, O>> + From>> EnsureOrigin - for EnsureSignedOneOrRoot -{ - type Success = ScheduledEnsureOriginSuccess; - fn try_origin(o: O) -> Result { - o.into().and_then(|o| match o { - RawOrigin::Root => Ok(ScheduledEnsureOriginSuccess::Root), - RawOrigin::Signed(1) => Ok(ScheduledEnsureOriginSuccess::Signed(1)), - r => Err(O::from(r)), - }) - } -} - -pub struct Executor; -impl DispatchCall for Executor { - fn dispatch_call( - signer: Option, - function: RuntimeCall, - ) -> Result< - Result>, - TransactionValidityError, - > { - let origin = match signer { - Some(who) => RuntimeOrigin::signed(who), - None => RuntimeOrigin::none(), - }; - Ok(function.dispatch(origin)) - } -} - -impl Config for Test { - type RuntimeEvent = RuntimeEvent; - type RuntimeOrigin = RuntimeOrigin; - type PalletsOrigin = OriginCaller; - type RuntimeCall = RuntimeCall; - type MaximumWeight = MaximumSchedulerWeight; - type ScheduleOrigin = EnsureSignedOneOrRoot; - type MaxScheduledPerBlock = ConstU32<10>; - type WeightInfo = TestWeightInfo; - type OriginPrivilegeCmp = EqualPrivilegeOnly; - type Preimages = (); - type PrioritySetOrigin = EnsureRoot; - type CallExecutor = Executor; -} - -pub type LoggerCall = logger::Call; - -pub fn new_test_ext() -> sp_io::TestExternalities { - let t = system::GenesisConfig::default() - .build_storage::() - .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() -} --- a/pallets/scheduler-v2/src/tests.rs +++ /dev/null @@ -1,874 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -// Original license: -// This file is part of Substrate. - -// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! # Scheduler tests. - -use super::*; -use crate::mock::{ - logger, new_test_ext, root, run_to_block, LoggerCall, RuntimeCall, Scheduler, Test, *, -}; -use frame_support::{ - assert_noop, assert_ok, - traits::{Contains, OnInitialize}, -}; - -#[test] -fn basic_scheduling_works() { - new_test_ext().execute_with(|| { - let call = RuntimeCall::Logger(LoggerCall::log { - i: 42, - weight: Weight::from_ref_time(10), - }); - assert!(!::BaseCallFilter::contains( - &call - )); - assert_ok!(Scheduler::do_schedule( - DispatchTime::At(4), - None, - 127, - root(), - >::new(call).unwrap(), - )); - 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 schedule_after_works() { - new_test_ext().execute_with(|| { - run_to_block(2); - let call = RuntimeCall::Logger(LoggerCall::log { - i: 42, - weight: Weight::from_ref_time(10), - }); - assert!(!::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(), - >::new(call).unwrap(), - )); - 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 = RuntimeCall::Logger(LoggerCall::log { - i: 42, - weight: Weight::from_ref_time(10), - }); - assert!(!::BaseCallFilter::contains( - &call - )); - assert_ok!(Scheduler::do_schedule( - DispatchTime::After(0), - None, - 127, - root(), - >::new(call).unwrap(), - )); - // 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(), - >::new(RuntimeCall::Logger(logger::Call::log { - i: 42, - weight: Weight::from_ref_time(10) - })) - .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_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 cancel_named_scheduling_works_with_normal_cancel() { - new_test_ext().execute_with(|| { - // at #4. - Scheduler::do_schedule_named( - [1u8; 32], - DispatchTime::At(4), - None, - 127, - root(), - >::new(RuntimeCall::Logger(LoggerCall::log { - i: 69, - weight: Weight::from_ref_time(10), - })) - .unwrap(), - ) - .unwrap(); - let i = Scheduler::do_schedule( - DispatchTime::At(4), - None, - 127, - root(), - >::new(RuntimeCall::Logger(LoggerCall::log { - i: 42, - weight: Weight::from_ref_time(10), - })) - .unwrap(), - ) - .unwrap(); - run_to_block(3); - assert!(logger::log().is_empty()); - assert_ok!(Scheduler::do_cancel_named(None, [1u8; 32])); - 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( - [1u8; 32], - DispatchTime::At(4), - Some((3, 3)), - 127, - root(), - >::new(RuntimeCall::Logger(LoggerCall::log { - i: 42, - weight: Weight::from_ref_time(10), - })) - .unwrap(), - ) - .unwrap(); - // same id results in error. - assert!(Scheduler::do_schedule_named( - [1u8; 32], - DispatchTime::At(4), - None, - 127, - root(), - >::new(RuntimeCall::Logger(LoggerCall::log { - i: 69, - weight: Weight::from_ref_time(10) - })) - .unwrap(), - ) - .is_err()); - // different id is ok. - Scheduler::do_schedule_named( - [2u8; 32], - DispatchTime::At(8), - None, - 127, - root(), - >::new(RuntimeCall::Logger(LoggerCall::log { - i: 69, - weight: Weight::from_ref_time(10), - })) - .unwrap(), - ) - .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, [1u8; 32])); - run_to_block(100); - assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 69u32)]); - }); -} - -#[test] -fn scheduler_respects_weight_limits() { - let max_weight: Weight = ::MaximumWeight::get(); - new_test_ext().execute_with(|| { - let call = RuntimeCall::Logger(LoggerCall::log { - i: 42, - weight: max_weight / 3 * 2, - }); - assert_ok!(Scheduler::do_schedule( - DispatchTime::At(4), - None, - 127, - root(), - >::new(call).unwrap(), - )); - let call = RuntimeCall::Logger(LoggerCall::log { - i: 69, - weight: max_weight / 3 * 2, - }); - assert_ok!(Scheduler::do_schedule( - DispatchTime::At(4), - None, - 127, - root(), - >::new(call).unwrap(), - )); - // 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)]); - }); -} - -/// Permanently overweight calls are not deleted but also not executed. -#[test] -fn scheduler_does_not_delete_permanently_overweight_call() { - let max_weight: Weight = ::MaximumWeight::get(); - new_test_ext().execute_with(|| { - let call = RuntimeCall::Logger(LoggerCall::log { - i: 42, - weight: max_weight, - }); - assert_ok!(Scheduler::do_schedule( - DispatchTime::At(4), - None, - 127, - root(), - >::new(call).unwrap(), - )); - // Never executes. - run_to_block(100); - assert_eq!(logger::log(), vec![]); - - // Assert the `PermanentlyOverweight` event. - assert_eq!( - System::events().last().unwrap().event, - crate::Event::PermanentlyOverweight { - task: (4, 0), - id: None - } - .into(), - ); - // The call is still in the agenda. - assert!(Agenda::::get(4)[0].is_some()); - }); -} - -#[test] -fn scheduler_handles_periodic_failure() { - let max_weight: Weight = ::MaximumWeight::get(); - let max_per_block = ::MaxScheduledPerBlock::get(); - - new_test_ext().execute_with(|| { - let call = RuntimeCall::Logger(LoggerCall::log { - i: 42, - weight: (max_weight / 3) * 2, - }); - let call = >::new(call).unwrap(); - - assert_ok!(Scheduler::do_schedule( - DispatchTime::At(4), - Some((4, u32::MAX)), - 127, - root(), - call.clone(), - )); - // Executes 5 times till block 20. - run_to_block(20); - assert_eq!(logger::log().len(), 5); - - // Block 28 will already be full. - for _ in 0..max_per_block { - assert_ok!(Scheduler::do_schedule( - DispatchTime::At(28), - None, - 120, - root(), - call.clone(), - )); - } - - // Going to block 24 will emit a `PeriodicFailed` event. - run_to_block(24); - assert_eq!(logger::log().len(), 6); - - assert_eq!( - System::events().last().unwrap().event, - crate::Event::PeriodicFailed { - task: (24, 0), - id: None - } - .into(), - ); - }); -} - -#[test] -fn scheduler_respects_priority_ordering() { - let max_weight: Weight = ::MaximumWeight::get(); - new_test_ext().execute_with(|| { - let call = RuntimeCall::Logger(LoggerCall::log { - i: 42, - weight: max_weight / 3, - }); - assert_ok!(Scheduler::do_schedule( - DispatchTime::At(4), - None, - 1, - root(), - >::new(call).unwrap(), - )); - let call = RuntimeCall::Logger(LoggerCall::log { - i: 69, - weight: max_weight / 3, - }); - assert_ok!(Scheduler::do_schedule( - DispatchTime::At(4), - None, - 0, - root(), - >::new(call).unwrap(), - )); - 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: Weight = ::MaximumWeight::get(); - let call = RuntimeCall::Logger(LoggerCall::log { - i: 42, - weight: max_weight / 5 * 2, - }); - assert_ok!(Scheduler::do_schedule( - DispatchTime::At(4), - None, - 255, - root(), - >::new(call).unwrap(), - )); - let call = RuntimeCall::Logger(LoggerCall::log { - i: 69, - weight: max_weight / 5 * 2, - }); - assert_ok!(Scheduler::do_schedule( - DispatchTime::At(4), - None, - 127, - root(), - >::new(call).unwrap(), - )); - let call = RuntimeCall::Logger(LoggerCall::log { - i: 2600, - weight: max_weight / 5 * 4, - }); - assert_ok!(Scheduler::do_schedule( - DispatchTime::At(4), - None, - 126, - root(), - >::new(call).unwrap(), - )); - - // 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 call_weight = Weight::from_ref_time(25); - - // Named - let call = RuntimeCall::Logger(LoggerCall::log { - i: 3, - weight: call_weight + Weight::from_ref_time(1), - }); - assert_ok!(Scheduler::do_schedule_named( - [1u8; 32], - DispatchTime::At(3), - None, - 255, - root(), - >::new(call).unwrap(), - )); - let call = RuntimeCall::Logger(LoggerCall::log { - i: 42, - weight: call_weight + Weight::from_ref_time(2), - }); - // Anon Periodic - assert_ok!(Scheduler::do_schedule( - DispatchTime::At(2), - Some((1000, 3)), - 128, - root(), - >::new(call).unwrap(), - )); - let call = RuntimeCall::Logger(LoggerCall::log { - i: 69, - weight: call_weight + Weight::from_ref_time(3), - }); - // Anon - assert_ok!(Scheduler::do_schedule( - DispatchTime::At(2), - None, - 127, - root(), - >::new(call).unwrap(), - )); - // Named Periodic - let call = RuntimeCall::Logger(LoggerCall::log { - i: 2600, - weight: call_weight + Weight::from_ref_time(4), - }); - assert_ok!(Scheduler::do_schedule_named( - [2u8; 32], - DispatchTime::At(1), - Some((1000, 3)), - 126, - root(), - >::new(call).unwrap(), - )); - - // Will include the named periodic only - assert_eq!( - Scheduler::on_initialize(1), - TestWeightInfo::service_agendas_base() - + TestWeightInfo::service_agenda_base(1) - + ::service_task(None, true, true) - + TestWeightInfo::execute_dispatch_unsigned() - + call_weight + Weight::from_ref_time(4) - ); - assert_eq!(IncompleteSince::::get(), None); - assert_eq!(logger::log(), vec![(root(), 2600u32)]); - - // Will include anon and anon periodic - assert_eq!( - Scheduler::on_initialize(2), - TestWeightInfo::service_agendas_base() - + TestWeightInfo::service_agenda_base(2) - + ::service_task(None, false, true) - + TestWeightInfo::execute_dispatch_unsigned() - + call_weight + Weight::from_ref_time(3) - + ::service_task(None, false, false) - + TestWeightInfo::execute_dispatch_unsigned() - + call_weight + Weight::from_ref_time(2) - ); - assert_eq!(IncompleteSince::::get(), None); - assert_eq!( - logger::log(), - vec![(root(), 2600u32), (root(), 69u32), (root(), 42u32)] - ); - - // Will include named only - assert_eq!( - Scheduler::on_initialize(3), - TestWeightInfo::service_agendas_base() - + TestWeightInfo::service_agenda_base(1) - + ::service_task(None, true, false) - + TestWeightInfo::execute_dispatch_unsigned() - + call_weight + Weight::from_ref_time(1) - ); - assert_eq!(IncompleteSince::::get(), None); - 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, - TestWeightInfo::service_agendas_base() + TestWeightInfo::service_agenda_base(0) - ); - }); -} - -#[test] -fn root_calls_works() { - new_test_ext().execute_with(|| { - let call = Box::new(RuntimeCall::Logger(LoggerCall::log { - i: 69, - weight: Weight::from_ref_time(10), - })); - let call2 = Box::new(RuntimeCall::Logger(LoggerCall::log { - i: 42, - weight: Weight::from_ref_time(10), - })); - assert_ok!(Scheduler::schedule_named( - RuntimeOrigin::root(), - [1u8; 32], - 4, - None, - Some(127), - call, - )); - assert_ok!(Scheduler::schedule( - RuntimeOrigin::root(), - 4, - None, - Some(127), - call2 - )); - run_to_block(3); - // Scheduled calls are in the agenda. - assert_eq!(Agenda::::get(4).len(), 2); - assert!(logger::log().is_empty()); - assert_ok!(Scheduler::cancel_named(RuntimeOrigin::root(), [1u8; 32])); - assert_ok!(Scheduler::cancel(RuntimeOrigin::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(RuntimeCall::Logger(LoggerCall::log { - i: 69, - weight: Weight::from_ref_time(10), - })); - let call2 = Box::new(RuntimeCall::Logger(LoggerCall::log { - i: 42, - weight: Weight::from_ref_time(10), - })); - let call3 = Box::new(RuntimeCall::Logger(LoggerCall::log { - i: 42, - weight: Weight::from_ref_time(10), - })); - - assert_noop!( - Scheduler::schedule_named(RuntimeOrigin::root(), [1u8; 32], 2, None, Some(127), call1), - Error::::TargetBlockNumberInPast, - ); - - assert_noop!( - Scheduler::schedule(RuntimeOrigin::root(), 2, None, Some(127), call2), - Error::::TargetBlockNumberInPast, - ); - - assert_noop!( - Scheduler::schedule(RuntimeOrigin::root(), 3, None, Some(127), call3), - Error::::TargetBlockNumberInPast, - ); - }); -} - -#[test] -fn should_use_origin() { - new_test_ext().execute_with(|| { - let call = Box::new(RuntimeCall::Logger(LoggerCall::log { - i: 69, - weight: Weight::from_ref_time(10), - })); - let call2 = Box::new(RuntimeCall::Logger(LoggerCall::log { - i: 42, - weight: Weight::from_ref_time(10), - })); - assert_ok!(Scheduler::schedule_named( - system::RawOrigin::Signed(1).into(), - [1u8; 32], - 4, - None, - None, - call, - )); - assert_ok!(Scheduler::schedule( - system::RawOrigin::Signed(1).into(), - 4, - None, - None, - call2, - )); - run_to_block(3); - // Scheduled calls are in the agenda. - assert_eq!(Agenda::::get(4).len(), 2); - assert!(logger::log().is_empty()); - assert_ok!(Scheduler::cancel_named( - system::RawOrigin::Signed(1).into(), - [1u8; 32] - )); - 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_origin() { - new_test_ext().execute_with(|| { - let call = Box::new(RuntimeCall::Logger(LoggerCall::log { - i: 69, - weight: Weight::from_ref_time(10), - })); - let call2 = Box::new(RuntimeCall::Logger(LoggerCall::log { - i: 42, - weight: Weight::from_ref_time(10), - })); - assert_noop!( - Scheduler::schedule_named( - system::RawOrigin::Signed(2).into(), - [1u8; 32], - 4, - None, - None, - call - ), - BadOrigin - ); - assert_noop!( - Scheduler::schedule(system::RawOrigin::Signed(2).into(), 4, None, None, call2), - BadOrigin - ); - }); -} - -#[test] -fn should_check_origin_for_cancel() { - new_test_ext().execute_with(|| { - let call = Box::new(RuntimeCall::Logger(LoggerCall::log_without_filter { - i: 69, - weight: Weight::from_ref_time(10), - })); - let call2 = Box::new(RuntimeCall::Logger(LoggerCall::log_without_filter { - i: 42, - weight: Weight::from_ref_time(10), - })); - assert_ok!(Scheduler::schedule_named( - system::RawOrigin::Signed(1).into(), - [1u8; 32], - 4, - None, - None, - call, - )); - assert_ok!(Scheduler::schedule( - system::RawOrigin::Signed(1).into(), - 4, - None, - None, - call2, - )); - run_to_block(3); - // Scheduled calls are in the agenda. - assert_eq!(Agenda::::get(4).len(), 2); - assert!(logger::log().is_empty()); - assert_noop!( - Scheduler::cancel_named(system::RawOrigin::Signed(2).into(), [1u8; 32]), - BadOrigin - ); - assert_noop!( - Scheduler::cancel(system::RawOrigin::Signed(2).into(), 4, 1), - BadOrigin - ); - assert_noop!( - Scheduler::cancel_named(system::RawOrigin::Root.into(), [1u8; 32]), - 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) - ] - ); - }); -} - -/// Cancelling a call and then scheduling a second call for the same -/// block results in different addresses. -#[test] -fn schedule_does_not_resuse_addr() { - new_test_ext().execute_with(|| { - let call = RuntimeCall::Logger(LoggerCall::log { - i: 42, - weight: Weight::from_ref_time(10), - }); - - // Schedule both calls. - let addr_1 = Scheduler::do_schedule( - DispatchTime::At(4), - None, - 127, - root(), - >::new(call.clone()).unwrap(), - ) - .unwrap(); - // Cancel the call. - assert_ok!(Scheduler::do_cancel(None, addr_1)); - let addr_2 = Scheduler::do_schedule( - DispatchTime::At(4), - None, - 127, - root(), - >::new(call).unwrap(), - ) - .unwrap(); - - // Should not re-use the address. - assert!(addr_1 != addr_2); - }); -} - -#[test] -fn schedule_agenda_overflows() { - let max: u32 = ::MaxScheduledPerBlock::get(); - - new_test_ext().execute_with(|| { - let call = RuntimeCall::Logger(LoggerCall::log { - i: 42, - weight: Weight::from_ref_time(10), - }); - let call = >::new(call).unwrap(); - - // Schedule the maximal number allowed per block. - for _ in 0..max { - Scheduler::do_schedule(DispatchTime::At(4), None, 127, root(), call.clone()).unwrap(); - } - - // One more time and it errors. - assert_noop!( - Scheduler::do_schedule(DispatchTime::At(4), None, 127, root(), call,), - >::AgendaIsExhausted, - ); - - run_to_block(4); - // All scheduled calls are executed. - assert_eq!(logger::log().len() as u32, max); - }); -} - -/// Cancelling and scheduling does not overflow the agenda but fills holes. -#[test] -fn cancel_and_schedule_fills_holes() { - let max: u32 = ::MaxScheduledPerBlock::get(); - assert!( - max > 3, - "This test only makes sense for MaxScheduledPerBlock > 3" - ); - - new_test_ext().execute_with(|| { - let call = RuntimeCall::Logger(LoggerCall::log { - i: 42, - weight: Weight::from_ref_time(10), - }); - let call = >::new(call).unwrap(); - let mut addrs = Vec::<_>::default(); - - // Schedule the maximal number allowed per block. - for _ in 0..max { - addrs.push( - Scheduler::do_schedule(DispatchTime::At(4), None, 127, root(), call.clone()) - .unwrap(), - ); - } - // Cancel three of them. - for addr in addrs.into_iter().take(3) { - Scheduler::do_cancel(None, addr).unwrap(); - } - // Schedule three new ones. - for i in 0..3 { - let (_block, index) = - Scheduler::do_schedule(DispatchTime::At(4), None, 127, root(), call.clone()) - .unwrap(); - assert_eq!(i, index); - } - - run_to_block(4); - // Maximum number of calls are executed. - assert_eq!(logger::log().len() as u32, max); - }); -} --- a/pallets/scheduler-v2/src/weights.rs +++ /dev/null @@ -1,270 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -// Original license: -// This file is part of Substrate. - -// Copyright (C) 2022 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Autogenerated weights for pallet_scheduler -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-10-03, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 - -// Executed Command: -// /home/benchbot/cargo_target_dir/production/substrate -// benchmark -// pallet -// --steps=50 -// --repeat=20 -// --extrinsic=* -// --execution=wasm -// --wasm-execution=compiled -// --heap-pages=4096 -// --pallet=pallet_scheduler -// --chain=dev -// --output=./frame/scheduler/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_scheduler. -pub trait WeightInfo { - fn service_agendas_base() -> Weight; - fn service_agenda_base(s: u32, ) -> Weight; - fn service_task_base() -> Weight; - fn service_task_fetched(s: u32, ) -> Weight; - fn service_task_named() -> Weight; - fn service_task_periodic() -> Weight; - fn execute_dispatch_signed() -> Weight; - fn execute_dispatch_unsigned() -> Weight; - fn schedule(s: u32, ) -> Weight; - fn cancel(s: u32, ) -> Weight; - fn schedule_named(s: u32, ) -> Weight; - fn cancel_named(s: u32, ) -> Weight; - fn change_named_priority(s: u32, ) -> Weight; -} - -/// Weights for pallet_scheduler using the Substrate node and recommended hardware. -pub struct SubstrateWeight(PhantomData); -impl WeightInfo for SubstrateWeight { - // Storage: Scheduler IncompleteSince (r:1 w:1) - fn service_agendas_base() -> Weight { - Weight::from_ref_time(4_992_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Scheduler Agenda (r:1 w:1) - /// The range of component `s` is `[0, 512]`. - fn service_agenda_base(s: u32, ) -> Weight { - Weight::from_ref_time(4_320_000 as u64) - // Standard Error: 619 - .saturating_add(Weight::from_ref_time(336_713 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - fn service_task_base() -> Weight { - Weight::from_ref_time(10_864_000 as u64) - } - // Storage: Preimage PreimageFor (r:1 w:1) - // Storage: Preimage StatusFor (r:1 w:1) - /// The range of component `s` is `[128, 4194304]`. - fn service_task_fetched(s: u32, ) -> Weight { - Weight::from_ref_time(24_586_000 as u64) - // Standard Error: 1 - .saturating_add(Weight::from_ref_time(1_138 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } - // Storage: Scheduler Lookup (r:0 w:1) - fn service_task_named() -> Weight { - Weight::from_ref_time(13_127_000 as u64) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - fn service_task_periodic() -> Weight { - Weight::from_ref_time(11_053_000 as u64) - } - fn execute_dispatch_signed() -> Weight { - Weight::from_ref_time(4_158_000 as u64) - } - fn execute_dispatch_unsigned() -> Weight { - Weight::from_ref_time(4_104_000 as u64) - } - // Storage: Scheduler Agenda (r:1 w:1) - /// The range of component `s` is `[0, 511]`. - fn schedule(s: u32, ) -> Weight { - Weight::from_ref_time(20_074_000 as u64) - // Standard Error: 765 - .saturating_add(Weight::from_ref_time(343_285 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Scheduler Agenda (r:1 w:1) - // Storage: Scheduler Lookup (r:0 w:1) - /// The range of component `s` is `[1, 512]`. - fn cancel(s: u32, ) -> Weight { - Weight::from_ref_time(21_509_000 as u64) - // Standard Error: 708 - .saturating_add(Weight::from_ref_time(323_013 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } - // Storage: Scheduler Lookup (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) - /// The range of component `s` is `[0, 511]`. - fn schedule_named(s: u32, ) -> Weight { - Weight::from_ref_time(22_427_000 as u64) - // Standard Error: 850 - .saturating_add(Weight::from_ref_time(357_265 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } - // Storage: Scheduler Lookup (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) - /// The range of component `s` is `[1, 512]`. - fn cancel_named(s: u32, ) -> Weight { - Weight::from_ref_time(22_875_000 as u64) - // Standard Error: 693 - .saturating_add(Weight::from_ref_time(336_643 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } - - // Storage: Scheduler Lookup (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) - fn change_named_priority(s: u32, ) -> Weight { - Weight::from_ref_time(8_642_000) - // Standard Error: 0 - .saturating_add(Weight::from_ref_time(431_000).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } -} - -// For backwards compatibility and tests -impl WeightInfo for () { - // Storage: Scheduler IncompleteSince (r:1 w:1) - fn service_agendas_base() -> Weight { - Weight::from_ref_time(4_992_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Scheduler Agenda (r:1 w:1) - /// The range of component `s` is `[0, 512]`. - fn service_agenda_base(s: u32, ) -> Weight { - Weight::from_ref_time(4_320_000 as u64) - // Standard Error: 619 - .saturating_add(Weight::from_ref_time(336_713 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - fn service_task_base() -> Weight { - Weight::from_ref_time(10_864_000 as u64) - } - // Storage: Preimage PreimageFor (r:1 w:1) - // Storage: Preimage StatusFor (r:1 w:1) - /// The range of component `s` is `[128, 4194304]`. - fn service_task_fetched(s: u32, ) -> Weight { - Weight::from_ref_time(24_586_000 as u64) - // Standard Error: 1 - .saturating_add(Weight::from_ref_time(1_138 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } - // Storage: Scheduler Lookup (r:0 w:1) - fn service_task_named() -> Weight { - Weight::from_ref_time(13_127_000 as u64) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - fn service_task_periodic() -> Weight { - Weight::from_ref_time(11_053_000 as u64) - } - fn execute_dispatch_signed() -> Weight { - Weight::from_ref_time(4_158_000 as u64) - } - fn execute_dispatch_unsigned() -> Weight { - Weight::from_ref_time(4_104_000 as u64) - } - // Storage: Scheduler Agenda (r:1 w:1) - /// The range of component `s` is `[0, 511]`. - fn schedule(s: u32, ) -> Weight { - Weight::from_ref_time(20_074_000 as u64) - // Standard Error: 765 - .saturating_add(Weight::from_ref_time(343_285 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Scheduler Agenda (r:1 w:1) - // Storage: Scheduler Lookup (r:0 w:1) - /// The range of component `s` is `[1, 512]`. - fn cancel(s: u32, ) -> Weight { - Weight::from_ref_time(21_509_000 as u64) - // Standard Error: 708 - .saturating_add(Weight::from_ref_time(323_013 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } - // Storage: Scheduler Lookup (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) - /// The range of component `s` is `[0, 511]`. - fn schedule_named(s: u32, ) -> Weight { - Weight::from_ref_time(22_427_000 as u64) - // Standard Error: 850 - .saturating_add(Weight::from_ref_time(357_265 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } - // Storage: Scheduler Lookup (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) - /// The range of component `s` is `[1, 512]`. - fn cancel_named(s: u32, ) -> Weight { - Weight::from_ref_time(22_875_000 as u64) - // Standard Error: 693 - .saturating_add(Weight::from_ref_time(336_643 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } - - // Storage: Scheduler Lookup (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) - fn change_named_priority(s: u32, ) -> Weight { - Weight::from_ref_time(8_642_000) - // Standard Error: 0 - .saturating_add(Weight::from_ref_time(431_000).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } -} --- a/primitives/data-structs/src/lib.rs +++ b/primitives/data-structs/src/lib.rs @@ -221,6 +221,7 @@ } /// Token data. +#[struct_versioning::versioned(version = 2, upper)] #[derive(Encode, Decode, Clone, PartialEq, TypeInfo)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct TokenData { @@ -231,6 +232,7 @@ pub owner: Option, /// Token pieces. + #[version(2.., upper(0))] pub pieces: u128, } --- a/primitives/rpc/src/lib.rs +++ b/primitives/rpc/src/lib.rs @@ -20,7 +20,7 @@ use up_data_structs::{ CollectionId, TokenId, RpcCollection, CollectionStats, CollectionLimits, Property, - PropertyKeyPermission, TokenData, TokenChild, RpcCollectionVersion1, + PropertyKeyPermission, TokenData, TokenChild, RpcCollectionVersion1, TokenDataVersion1, }; use sp_std::vec::Vec; @@ -77,6 +77,13 @@ keys: Option>> ) -> Result>; + #[changed_in(3)] + fn token_data( + collection: CollectionId, + token_id: TokenId, + keys: Option>> + ) -> Result>; + /// Total number of tokens in collection. fn total_supply(collection: CollectionId) -> Result; --- a/runtime/common/config/pallets/scheduler.rs +++ b/runtime/common/config/pallets/scheduler.rs @@ -27,7 +27,7 @@ runtime_common::{scheduler::SchedulerPaymentExecutor, config::substrate::RuntimeBlockWeights}, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, OriginCaller, Balances, }; -use pallet_unique_scheduler_v2::ScheduledEnsureOriginSuccess; +use pallet_unique_scheduler::ScheduledEnsureOriginSuccess; use up_common::types::AccountId; parameter_types! { @@ -70,34 +70,19 @@ } } -// impl pallet_unique_scheduler::Config for Runtime { -// type RuntimeEvent = RuntimeEvent; -// type RuntimeOrigin = RuntimeOrigin; -// type Currency = Balances; -// type PalletsOrigin = OriginCaller; -// type RuntimeCall = RuntimeCall; -// type MaximumWeight = MaximumSchedulerWeight; -// type ScheduleOrigin = EnsureSignedOrRoot; -// type PrioritySetOrigin = EnsureRoot; -// type MaxScheduledPerBlock = MaxScheduledPerBlock; -// type WeightInfo = (); -// type CallExecutor = SchedulerPaymentExecutor; -// type OriginPrivilegeCmp = EqualOrRootOnly; -// type PreimageProvider = (); -// type NoPreimagePostponement = NoPreimagePostponement; -// } - -impl pallet_unique_scheduler_v2::Config for Runtime { +impl pallet_unique_scheduler::Config for Runtime { type RuntimeEvent = RuntimeEvent; type RuntimeOrigin = RuntimeOrigin; + type Currency = Balances; type PalletsOrigin = OriginCaller; type RuntimeCall = RuntimeCall; type MaximumWeight = MaximumSchedulerWeight; type ScheduleOrigin = EnsureSignedOrRoot; - type OriginPrivilegeCmp = EqualOrRootOnly; + type PrioritySetOrigin = EnsureRoot; type MaxScheduledPerBlock = MaxScheduledPerBlock; type WeightInfo = (); - type Preimages = (); type CallExecutor = SchedulerPaymentExecutor; - type PrioritySetOrigin = EnsureRoot; + type OriginPrivilegeCmp = EqualOrRootOnly; + type PreimageProvider = (); + type NoPreimagePostponement = NoPreimagePostponement; } --- a/runtime/common/construct_runtime/mod.rs +++ b/runtime/common/construct_runtime/mod.rs @@ -57,8 +57,8 @@ Inflation: pallet_inflation::{Pallet, Call, Storage} = 60, Unique: pallet_unique::{Pallet, Call, Storage, Event} = 61, - // #[runtimes(opal)] - // Scheduler: pallet_unique_scheduler::{Pallet, Call, Storage, Event} = 62, + #[runtimes(opal)] + Scheduler: pallet_unique_scheduler::{Pallet, Call, Storage, Event} = 62, Configuration: pallet_configuration::{Pallet, Call, Storage} = 63, @@ -93,9 +93,6 @@ EvmContractHelpers: pallet_evm_contract_helpers::{Pallet, Storage, Event} = 151, EvmTransactionPayment: pallet_evm_transaction_payment::{Pallet} = 152, EvmMigration: pallet_evm_migration::{Pallet, Call, Storage} = 153, - - #[runtimes(opal)] - Scheduler: pallet_unique_scheduler_v2::{Pallet, Call, Storage, Event} = 154, #[runtimes(opal)] TestUtils: pallet_test_utils = 255, --- a/runtime/common/scheduler.rs +++ b/runtime/common/scheduler.rs @@ -28,11 +28,11 @@ use crate::{Runtime, RuntimeCall, RuntimeOrigin, Balances}; use up_common::types::{AccountId, Balance}; use fp_self_contained::SelfContainedCall; -use pallet_unique_scheduler_v2::DispatchCall; +use pallet_unique_scheduler::DispatchCall; use pallet_transaction_payment::ChargeTransactionPayment; -// type SponsorshipChargeTransactionPayment = -// pallet_charge_transaction::ChargeTransactionPayment; +type SponsorshipChargeTransactionPayment = + pallet_charge_transaction::ChargeTransactionPayment; /// The SignedExtension to the basic transaction logic. pub type SignedExtraScheduler = ( @@ -59,7 +59,7 @@ pub struct SchedulerPaymentExecutor; -impl +impl DispatchCall for SchedulerPaymentExecutor where ::RuntimeCall: Member @@ -69,13 +69,13 @@ + From>, SelfContainedSignedInfo: Send + Sync + 'static, RuntimeCall: From<::RuntimeCall> - + From<::RuntimeCall> + + From<::RuntimeCall> + SelfContainedCall, sp_runtime::AccountId32: From<::AccountId>, { fn dispatch_call( signer: Option<::AccountId>, - call: ::RuntimeCall, + call: ::RuntimeCall, ) -> Result< Result>, TransactionValidityError, @@ -103,98 +103,52 @@ extrinsic.apply::(&dispatch_info, len) } -} -// impl -// DispatchCall for SchedulerPaymentExecutor -// where -// ::Call: Member -// + Dispatchable -// + SelfContainedCall -// + GetDispatchInfo -// + From>, -// SelfContainedSignedInfo: Send + Sync + 'static, -// Call: From<::Call> -// + From<::Call> -// + SelfContainedCall, -// sp_runtime::AccountId32: From<::AccountId>, -// { -// fn dispatch_call( -// signer: Option<::AccountId>, -// call: ::Call, -// ) -> Result< -// Result>, -// TransactionValidityError, -// > { -// let dispatch_info = call.get_dispatch_info(); -// let len = call.encoded_size(); + fn reserve_balance( + id: [u8; 16], + sponsor: ::AccountId, + call: ::RuntimeCall, + count: u32, + ) -> Result<(), DispatchError> { + let dispatch_info = call.get_dispatch_info(); + let weight: Balance = + SponsorshipChargeTransactionPayment::traditional_fee(0, &dispatch_info, 0) + .saturating_mul(count.into()); -// let signed = match signer { -// Some(signer) => fp_self_contained::CheckedSignature::Signed( -// signer.clone().into(), -// get_signed_extras(signer.into()), -// ), -// None => fp_self_contained::CheckedSignature::Unsigned, -// }; + >::reserve_named( + &id, + &(sponsor.into()), + weight, + ) + } -// let extrinsic = fp_self_contained::CheckedExtrinsic::< -// AccountId, -// Call, -// SignedExtraScheduler, -// SelfContainedSignedInfo, -// > { -// signed, -// function: call.into(), -// }; - -// extrinsic.apply::(&dispatch_info, len) -// } - -// fn reserve_balance( -// id: [u8; 16], -// sponsor: ::AccountId, -// call: ::Call, -// count: u32, -// ) -> Result<(), DispatchError> { -// let dispatch_info = call.get_dispatch_info(); -// let weight: Balance = -// SponsorshipChargeTransactionPayment::traditional_fee(0, &dispatch_info, 0) -// .saturating_mul(count.into()); + fn pay_for_call( + id: [u8; 16], + sponsor: ::AccountId, + call: ::RuntimeCall, + ) -> Result { + let dispatch_info = call.get_dispatch_info(); + let weight: Balance = + SponsorshipChargeTransactionPayment::traditional_fee(0, &dispatch_info, 0); + Ok( + >::unreserve_named( + &id, + &(sponsor.into()), + weight, + ), + ) + } -// >::reserve_named( -// &id, -// &(sponsor.into()), -// weight, -// ) -// } - -// fn pay_for_call( -// id: [u8; 16], -// sponsor: ::AccountId, -// call: ::Call, -// ) -> Result { -// let dispatch_info = call.get_dispatch_info(); -// let weight: Balance = -// SponsorshipChargeTransactionPayment::traditional_fee(0, &dispatch_info, 0); -// Ok( -// >::unreserve_named( -// &id, -// &(sponsor.into()), -// weight, -// ), -// ) -// } - -// fn cancel_reserve( -// id: [u8; 16], -// sponsor: ::AccountId, -// ) -> Result { -// Ok( -// >::unreserve_named( -// &id, -// &(sponsor.into()), -// u128::MAX, -// ), -// ) -// } -// } + fn cancel_reserve( + id: [u8; 16], + sponsor: ::AccountId, + ) -> Result { + Ok( + >::unreserve_named( + &id, + &(sponsor.into()), + u128::MAX, + ), + ) + } +} --- a/runtime/opal/Cargo.toml +++ b/runtime/opal/Cargo.toml @@ -42,7 +42,6 @@ 'pallet-inflation/runtime-benchmarks', 'pallet-app-promotion/runtime-benchmarks', 'pallet-unique-scheduler/runtime-benchmarks', - 'pallet-unique-scheduler-v2/runtime-benchmarks', 'pallet-xcm/runtime-benchmarks', 'sp-runtime/runtime-benchmarks', 'xcm-builder/runtime-benchmarks', @@ -141,7 +140,6 @@ 'pallet-proxy-rmrk-equip/std', 'pallet-unique/std', 'pallet-unique-scheduler/std', - 'pallet-unique-scheduler-v2/std', 'pallet-charge-transaction/std', 'up-data-structs/std', 'sp-api/std', @@ -474,7 +472,6 @@ pallet-proxy-rmrk-equip = { default-features = false, path = "../../pallets/proxy-rmrk-equip", package = "pallet-rmrk-equip" } pallet-unique-scheduler = { path = '../../pallets/scheduler', default-features = false } pallet-charge-transaction = { git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = "polkadot-v0.9.30", package = "pallet-template-transaction-payment", default-features = false, version = "3.0.0" } -pallet-unique-scheduler-v2 = { path = '../../pallets/scheduler-v2', default-features = false } pallet-evm-migration = { path = '../../pallets/evm-migration', default-features = false } pallet-evm-contract-helpers = { path = '../../pallets/evm-contract-helpers', default-features = false } pallet-evm-transaction-payment = { path = '../../pallets/evm-transaction-payment', default-features = false } --- a/test-pallets/utils/Cargo.toml +++ b/test-pallets/utils/Cargo.toml @@ -10,8 +10,7 @@ scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-support = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.30" } frame-system = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.30" } -# pallet-unique-scheduler = { path = '../../pallets/scheduler', default-features = false } -pallet-unique-scheduler-v2 = { path = '../../pallets/scheduler-v2', default-features = false } +pallet-unique-scheduler = { path = '../../pallets/scheduler', default-features = false } [features] default = ["std"] @@ -20,5 +19,5 @@ "scale-info/std", "frame-support/std", "frame-system/std", - "pallet-unique-scheduler-v2/std", + "pallet-unique-scheduler/std", ] --- a/test-pallets/utils/src/lib.rs +++ b/test-pallets/utils/src/lib.rs @@ -24,10 +24,10 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; - use pallet_unique_scheduler_v2::{TaskName, Pallet as SchedulerPallet}; + use pallet_unique_scheduler::{ScheduledId, Pallet as SchedulerPallet}; #[pallet::config] - pub trait Config: frame_system::Config + pallet_unique_scheduler_v2::Config { + pub trait Config: frame_system::Config + pallet_unique_scheduler::Config { type RuntimeEvent: From> + IsType<::RuntimeEvent>; } @@ -94,13 +94,14 @@ #[pallet::weight(10_000)] pub fn self_canceling_inc( origin: OriginFor, - id: TaskName, + id: ScheduledId, max_test_value: u32, ) -> DispatchResult { Self::ensure_origin_and_enabled(origin.clone())?; - Self::inc_test_value(origin.clone())?; - if >::get() == max_test_value { + if >::get() < max_test_value { + Self::inc_test_value(origin)?; + } else { SchedulerPallet::::cancel_named(origin, id)?; } --- a/tests/src/util/playgrounds/unique.dev.ts +++ b/tests/src/util/playgrounds/unique.dev.ts @@ -308,7 +308,7 @@ await this.helper.wait.noScheduledTasks(); function makeId(slider: number) { - const scheduledIdSize = 64; + const scheduledIdSize = 32; const hexId = slider.toString(16); const prefixSize = scheduledIdSize - hexId.length; --- a/tests/src/util/playgrounds/unique.ts +++ b/tests/src/util/playgrounds/unique.ts @@ -2838,7 +2838,7 @@ this.blocksNum, this.options.periodic ? [this.options.periodic.period, this.options.periodic.repetitions] : null, this.options.priority ?? null, - scheduledTx, + {Value: scheduledTx}, ], expectSuccess, );