--- a/Makefile +++ b/Makefile @@ -93,5 +93,9 @@ bench-structure: make _bench PALLET=structure +.PHONY: bench-scheduler +bench-scheduler: + make _bench PALLET=unq-scheduler + .PHONY: bench -bench: bench-evm-migration bench-unique bench-structure bench-fungible bench-refungible bench-nonfungible +bench: bench-evm-migration bench-unique bench-structure bench-fungible bench-refungible bench-nonfungible bench-scheduler --- a/pallets/fungible/src/weights.rs +++ b/pallets/fungible/src/weights.rs @@ -3,12 +3,13 @@ //! Autogenerated weights for pallet_fungible //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-03-01, STEPS: `50`, REPEAT: 200, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-06-09, STEPS: `50`, REPEAT: 200, LOW RANGE: `[]`, HIGH RANGE: `[]` //! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 // Executed Command: // target/release/unique-collator // benchmark +// pallet // --pallet // pallet-fungible // --wasm-execution @@ -47,16 +48,16 @@ // Storage: Fungible TotalSupply (r:1 w:1) // Storage: Fungible Balance (r:1 w:1) fn create_item() -> Weight { - (14_407_000 as Weight) + (40_678_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } // Storage: Fungible TotalSupply (r:1 w:1) // Storage: Fungible Balance (r:4 w:4) fn create_multiple_items_ex(b: u32, ) -> Weight { - (13_030_000 as Weight) - // Standard Error: 1_000 - .saturating_add((3_779_000 as Weight).saturating_mul(b as Weight)) + (13_036_000 as Weight) + // Standard Error: 9_000 + .saturating_add((15_611_000 as Weight).saturating_mul(b as Weight)) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(b as Weight))) .saturating_add(T::DbWeight::get().writes(1 as Weight)) @@ -65,28 +66,27 @@ // Storage: Fungible TotalSupply (r:1 w:1) // Storage: Fungible Balance (r:1 w:1) fn burn_item() -> Weight { - (15_565_000 as Weight) + (42_340_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } - // Storage: Fungible Balance (r:2 w:2) fn transfer() -> Weight { - (17_713_000 as Weight) + (47_321_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } // Storage: Fungible Balance (r:1 w:0) // Storage: Fungible Allowance (r:0 w:1) fn approve() -> Weight { - (14_834_000 as Weight) + (38_385_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } // Storage: Fungible Allowance (r:1 w:1) // Storage: Fungible Balance (r:2 w:2) fn transfer_from() -> Weight { - (25_189_000 as Weight) + (69_001_000 as Weight) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } @@ -94,7 +94,7 @@ // Storage: Fungible TotalSupply (r:1 w:1) // Storage: Fungible Balance (r:1 w:1) fn burn_from() -> Weight { - (24_065_000 as Weight) + (63_996_000 as Weight) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } @@ -105,16 +105,16 @@ // Storage: Fungible TotalSupply (r:1 w:1) // Storage: Fungible Balance (r:1 w:1) fn create_item() -> Weight { - (14_407_000 as Weight) + (40_678_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } // Storage: Fungible TotalSupply (r:1 w:1) // Storage: Fungible Balance (r:4 w:4) fn create_multiple_items_ex(b: u32, ) -> Weight { - (13_030_000 as Weight) - // Standard Error: 1_000 - .saturating_add((3_779_000 as Weight).saturating_mul(b as Weight)) + (13_036_000 as Weight) + // Standard Error: 9_000 + .saturating_add((15_611_000 as Weight).saturating_mul(b as Weight)) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(b as Weight))) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) @@ -123,28 +123,27 @@ // Storage: Fungible TotalSupply (r:1 w:1) // Storage: Fungible Balance (r:1 w:1) fn burn_item() -> Weight { - (15_565_000 as Weight) + (42_340_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } - // Storage: Fungible Balance (r:2 w:2) fn transfer() -> Weight { - (17_713_000 as Weight) + (47_321_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } // Storage: Fungible Balance (r:1 w:0) // Storage: Fungible Allowance (r:0 w:1) fn approve() -> Weight { - (14_834_000 as Weight) + (38_385_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } // Storage: Fungible Allowance (r:1 w:1) // Storage: Fungible Balance (r:2 w:2) fn transfer_from() -> Weight { - (25_189_000 as Weight) + (69_001_000 as Weight) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } @@ -152,7 +151,7 @@ // Storage: Fungible TotalSupply (r:1 w:1) // Storage: Fungible Balance (r:1 w:1) fn burn_from() -> Weight { - (24_065_000 as Weight) + (63_996_000 as Weight) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } --- a/pallets/scheduler/src/benchmarking.rs +++ b/pallets/scheduler/src/benchmarking.rs @@ -1,23 +1,6 @@ -// 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-2021 Parity Technologies (UK) Ltd. +// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -33,91 +16,203 @@ // limitations under the License. //! Scheduler pallet benchmarking. - -#![cfg(feature = "runtime-benchmarks")] use super::*; -use sp_std::{vec, prelude::*}; +use frame_benchmarking::benchmarks; +use frame_support::{ + ensure, + traits::{OnInitialize, PreimageProvider, PreimageRecipient}, +}; use frame_system::RawOrigin; -use frame_support::{ensure, traits::OnInitialize}; -use frame_benchmarking::{benchmarks, impl_benchmark_test_suite}; +use sp_runtime::traits::Hash; +use sp_std::{prelude::*, vec}; -use crate::Module as Scheduler; +use crate::Pallet as Scheduler; use frame_system::Pallet as System; const BLOCK_NUMBER: u32 = 2; -// Add `n` named items to the schedule -fn fill_schedule(when: T::BlockNumber, n: u32) -> Result<(), &'static str> { - // Essentially a no-op call. - let call = frame_system::Call::set_storage { items: vec![] }; +/// Add `n` named items to the schedule. +/// +/// For `resolved`: +/// - `None`: aborted (hash without preimage) +/// - `Some(true)`: hash resolves into call if possible, plain call otherwise +/// - `Some(false)`: plain call +fn fill_schedule( + when: T::BlockNumber, + n: u32, + periodic: bool, + named: bool, + resolved: Option, +) -> Result<(), &'static str> { for i in 0..n { // Named schedule is strictly heavier than anonymous - Scheduler::::do_schedule_named( - i.encode(), - DispatchTime::At(when), - // Add periodicity - Some((T::BlockNumber::one(), 100)), - // HARD_DEADLINE priority means it gets executed no matter what - 0, - frame_system::RawOrigin::Root.into(), - call.clone().into(), - )?; + let (call, hash) = call_and_hash::(i); + let call_or_hash = match resolved { + Some(true) => { + T::PreimageProvider::note_preimage(call.encode().try_into().unwrap()); + if T::PreimageProvider::have_preimage(&hash) { + CallOrHashOf::::Hash(hash) + } else { + call.into() + } + }, + Some(false) => call.into(), + None => CallOrHashOf::::Hash(hash), + }; + let period = match periodic { + true => Some(((i + 100).into(), 100)), + false => None, + }; + let t = DispatchTime::At(when); + let origin = frame_system::RawOrigin::Root.into(); + if named { + Scheduler::::do_schedule_named(i.encode().try_into().unwrap_or([0; MAX_TASK_ID_LENGTH_IN_BYTES as usize]), + t, period, 0, origin, call_or_hash)?; + } else { + Scheduler::::do_schedule(t, period, 0, origin, call_or_hash)?; + } } - ensure!( - Agenda::::get(when).len() == n as usize, - "didn't fill schedule" - ); + ensure!(Agenda::::get(when).len() == n as usize, "didn't fill schedule"); Ok(()) } +fn call_and_hash(i: u32) -> (::Call, T::Hash) { + // Essentially a no-op call. + let call: ::Call = frame_system::Call::remark { remark: i.encode() }.into(); + let hash = T::Hashing::hash_of(&call); + (call, hash) +} + + benchmarks! { - schedule { - let s in 0 .. T::MaxScheduledPerBlock::get(); + on_initialize_periodic_named_resolved { + let s in 1 .. T::MaxScheduledPerBlock::get(); + let when = BLOCK_NUMBER.into(); + fill_schedule::(when, s, true, true, Some(true))?; + }: { Scheduler::::on_initialize(BLOCK_NUMBER.into()); } + verify { + assert_eq!(System::::event_count(), s * 2); + for i in 0..s { + assert_eq!(Agenda::::get(when + (i + 100).into()).len(), 1 as usize); + } + } + + on_initialize_named_resolved { + let s in 1 .. T::MaxScheduledPerBlock::get(); + let when = BLOCK_NUMBER.into(); + fill_schedule::(when, s, false, true, Some(true))?; + }: { Scheduler::::on_initialize(BLOCK_NUMBER.into()); } + verify { + assert_eq!(System::::event_count(), s * 2); + assert!(Agenda::::iter().count() == 0); + } + + on_initialize_periodic_resolved { + let s in 1 .. T::MaxScheduledPerBlock::get(); + let when = BLOCK_NUMBER.into(); + fill_schedule::(when, s, true, false, Some(true))?; + }: { Scheduler::::on_initialize(BLOCK_NUMBER.into()); } + verify { + assert_eq!(System::::event_count(), s * 2); + for i in 0..s { + assert_eq!(Agenda::::get(when + (i + 100).into()).len(), 1 as usize); + } + } + + on_initialize_resolved { + let s in 1 .. T::MaxScheduledPerBlock::get(); let when = BLOCK_NUMBER.into(); - let periodic = Some((T::BlockNumber::one(), 100)); - let priority = 0; - // Essentially a no-op call. - let call = Box::new(frame_system::Call::set_storage { items: vec![] }.into()); + fill_schedule::(when, s, false, false, Some(true))?; + }: { Scheduler::::on_initialize(BLOCK_NUMBER.into()); } + verify { + assert_eq!(System::::event_count(), s * 2); + assert!(Agenda::::iter().count() == 0); + } - fill_schedule::(when, s)?; - }: _(RawOrigin::Root, when, periodic, priority, call) + on_initialize_named_aborted { + let s in 1 .. T::MaxScheduledPerBlock::get(); + let when = BLOCK_NUMBER.into(); + fill_schedule::(when, s, false, true, None)?; + }: { Scheduler::::on_initialize(BLOCK_NUMBER.into()); } verify { - ensure!( - Agenda::::get(when).len() == (s + 1) as usize, - "didn't add to schedule" - ); + assert_eq!(System::::event_count(), 0); + if let Some(delay) = T::NoPreimagePostponement::get() { + assert_eq!(Agenda::::get(when + delay).len(), s as usize); + } else { + assert!(Agenda::::iter().count() == 0); + } + } + + on_initialize_aborted { + let s in 1 .. T::MaxScheduledPerBlock::get(); + let when = BLOCK_NUMBER.into(); + fill_schedule::(when, s, false, false, None)?; + }: { Scheduler::::on_initialize(BLOCK_NUMBER.into()); } + verify { + assert_eq!(System::::event_count(), 0); + if let Some(delay) = T::NoPreimagePostponement::get() { + assert_eq!(Agenda::::get(when + delay).len(), s as usize); + } else { + assert!(Agenda::::iter().count() == 0); + } + } + + on_initialize_periodic_named { + let s in 1 .. T::MaxScheduledPerBlock::get(); + let when = BLOCK_NUMBER.into(); + fill_schedule::(when, s, true, true, Some(false))?; + }: { Scheduler::::on_initialize(BLOCK_NUMBER.into()); } + verify { + assert_eq!(System::::event_count(), s); + for i in 0..s { + assert_eq!(Agenda::::get(when + (i + 100).into()).len(), 1 as usize); + } + } + + on_initialize_periodic { + let s in 1 .. T::MaxScheduledPerBlock::get(); + let when = BLOCK_NUMBER.into(); + fill_schedule::(when, s, true, false, Some(false))?; + }: { Scheduler::::on_initialize(when); } + verify { + assert_eq!(System::::event_count(), s); + for i in 0..s { + assert_eq!(Agenda::::get(when + (i + 100).into()).len(), 1 as usize); + } } - cancel { + on_initialize_named { let s in 1 .. T::MaxScheduledPerBlock::get(); let when = BLOCK_NUMBER.into(); + fill_schedule::(when, s, false, true, Some(false))?; + }: { Scheduler::::on_initialize(BLOCK_NUMBER.into()); } + verify { + assert_eq!(System::::event_count(), s); + assert!(Agenda::::iter().count() == 0); + } - fill_schedule::(when, s)?; - assert_eq!(Agenda::::get(when).len(), s as usize); - }: _(RawOrigin::Root, when, 0) + on_initialize { + let s in 1 .. T::MaxScheduledPerBlock::get(); + let when = BLOCK_NUMBER.into(); + fill_schedule::(when, s, false, false, Some(false))?; + }: { Scheduler::::on_initialize(BLOCK_NUMBER.into()); } verify { - ensure!( - Lookup::::get(0.encode()).is_none(), - "didn't remove from lookup" - ); - // Removed schedule is NONE - ensure!( - Agenda::::get(when)[0].is_none(), - "didn't remove from schedule" - ); + assert_eq!(System::::event_count(), s); + assert!(Agenda::::iter().count() == 0); } schedule_named { let s in 0 .. T::MaxScheduledPerBlock::get(); - let id = s.encode(); + let id = s.encode().try_into().unwrap_or([0; MAX_TASK_ID_LENGTH_IN_BYTES as usize]); let when = BLOCK_NUMBER.into(); let periodic = Some((T::BlockNumber::one(), 100)); let priority = 0; // Essentially a no-op call. - let call = Box::new(frame_system::Call::set_storage { items: vec![] }.into()); + let inner_call = frame_system::Call::set_storage { items: vec![] }.into(); + let call = Box::new(CallOrHashOf::::Value(inner_call)); - fill_schedule::(when, s)?; + fill_schedule::(when, s, true, true, Some(false))?; }: _(RawOrigin::Root, id, when, periodic, priority, call) verify { ensure!( @@ -129,12 +224,13 @@ cancel_named { let s in 1 .. T::MaxScheduledPerBlock::get(); let when = BLOCK_NUMBER.into(); + let id = 0.encode().try_into().unwrap_or([0; MAX_TASK_ID_LENGTH_IN_BYTES as usize]); - fill_schedule::(when, s)?; - }: _(RawOrigin::Root, 0.encode()) + fill_schedule::(when, s, true, true, Some(false))?; + }: _(RawOrigin::Root, id) verify { ensure!( - Lookup::::get(0.encode()).is_none(), + Lookup::::get(id).is_none(), "didn't remove from lookup" ); // Removed schedule is NONE @@ -144,21 +240,5 @@ ); } - // TODO [#7141]: Make this more complex and flexible so it can be used in automation. - #[extra] - on_initialize { - let s in 0 .. T::MaxScheduledPerBlock::get(); - let when = BLOCK_NUMBER.into(); - fill_schedule::(when, s)?; - }: { Scheduler::::on_initialize(BLOCK_NUMBER.into()); } - verify { - assert_eq!(System::::event_count(), s); - // Next block should have all the schedules again - ensure!( - Agenda::::get(when + T::BlockNumber::one()).len() == s as usize, - "didn't append schedule" - ); - } + impl_benchmark_test_suite!(Scheduler, crate::mock::new_test_ext(), crate::mock::Test); } - -impl_benchmark_test_suite!(Scheduler, crate::tests::new_test_ext(), crate::tests::Test,); --- a/pallets/scheduler/src/lib.rs +++ b/pallets/scheduler/src/lib.rs @@ -60,9 +60,8 @@ // Ensure we're `no_std` when compiling for Wasm. #![cfg_attr(not(feature = "std"), no_std)] -// FIXME -// #[cfg(feature = "runtime-benchmarks")] -// mod benchmarking; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; pub mod weights; @@ -639,66 +638,7 @@ Ok((when, index)) } - - 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 { - s.call.ensure_unrequested::(); - if let Some(id) = s.maybe_id { - Lookup::::remove(id); - } - Self::deposit_event(Event::Canceled { when, index }); - Ok(()) - } else { - Err(Error::::NotFound)? - } - } - - fn do_reschedule( - (when, index): TaskAddress, - new_time: DispatchTime, - ) -> Result, DispatchError> { - let new_time = Self::resolve_time(new_time)?; - - if new_time == when { - return Err(Error::::RescheduleNoChange.into()); - } - Agenda::::try_mutate(when, |agenda| -> DispatchResult { - let task = agenda.get_mut(index as usize).ok_or(Error::::NotFound)?; - let task = task.take().ok_or(Error::::NotFound)?; - Agenda::::append(new_time, Some(task)); - Ok(()) - })?; - - let new_index = Agenda::::decode_len(new_time).unwrap_or(1) as u32 - 1; - Self::deposit_event(Event::Canceled { when, index }); - Self::deposit_event(Event::Scheduled { - when: new_time, - index: new_index, - }); - - Ok((new_time, new_index)) - } - fn do_schedule_named( id: ScheduledId, when: DispatchTime, @@ -790,124 +730,3 @@ } }) } - - fn do_reschedule_named( - id: ScheduledId, - new_time: DispatchTime, - ) -> Result, DispatchError> { - let new_time = Self::resolve_time(new_time)?; - - Lookup::::try_mutate_exists( - id, - |lookup| -> Result, DispatchError> { - let (when, index) = lookup.ok_or(Error::::NotFound)?; - - if new_time == when { - return Err(Error::::RescheduleNoChange.into()); - } - - Agenda::::try_mutate(when, |agenda| -> DispatchResult { - let task = agenda.get_mut(index as usize).ok_or(Error::::NotFound)?; - let task = task.take().ok_or(Error::::NotFound)?; - Agenda::::append(new_time, Some(task)); - - Ok(()) - })?; - - let new_index = Agenda::::decode_len(new_time).unwrap_or(1) as u32 - 1; - Self::deposit_event(Event::Canceled { when, index }); - Self::deposit_event(Event::Scheduled { - when: new_time, - index: new_index, - }); - - *lookup = Some((new_time, new_index)); - - Ok((new_time, new_index)) - }, - ) - } -} - -impl schedule::v2::Anon::Call, T::PalletsOrigin> - for Pallet -{ - type Address = TaskAddress; - type Hash = T::Hash; - - fn schedule( - when: DispatchTime, - maybe_periodic: Option>, - priority: schedule::Priority, - origin: T::PalletsOrigin, - call: CallOrHashOf, - ) -> Result { - Self::do_schedule(when, maybe_periodic, priority, origin, call) - } - - fn cancel((when, index): Self::Address) -> Result<(), ()> { - Self::do_cancel(None, (when, index)).map_err(|_| ()) - } - - fn reschedule( - address: Self::Address, - when: DispatchTime, - ) -> Result { - Self::do_reschedule(address, when) - } - - fn next_dispatch_time((when, index): Self::Address) -> Result { - Agenda::::get(when) - .get(index as usize) - .ok_or(()) - .map(|_| when) - } -} - -impl schedule::v2::Named::Call, T::PalletsOrigin> - for Pallet -{ - type Address = TaskAddress; - type Hash = T::Hash; - - fn schedule_named( - id: Vec, - when: DispatchTime, - maybe_periodic: Option>, - priority: schedule::Priority, - origin: T::PalletsOrigin, - call: CallOrHashOf, - ) -> Result { - let inner_id: ScheduledId = id - .try_into() - .unwrap_or([0; MAX_TASK_ID_LENGTH_IN_BYTES as usize]); - Self::do_schedule_named(inner_id, when, maybe_periodic, priority, origin, call) - .map_err(|_| ()) - } - - fn cancel_named(id: Vec) -> Result<(), ()> { - let inner_id: ScheduledId = id - .try_into() - .unwrap_or([0; MAX_TASK_ID_LENGTH_IN_BYTES as usize]); - Self::do_cancel_named(None, inner_id).map_err(|_| ()) - } - - fn reschedule_named( - id: Vec, - when: DispatchTime, - ) -> Result { - let inner_id: ScheduledId = id - .try_into() - .unwrap_or([0; MAX_TASK_ID_LENGTH_IN_BYTES as usize]); - Self::do_reschedule_named(inner_id, when) - } - - fn next_dispatch_time(id: Vec) -> Result { - let inner_id: ScheduledId = id - .try_into() - .unwrap_or([0; MAX_TASK_ID_LENGTH_IN_BYTES as usize]); - Lookup::::get(inner_id) - .and_then(|(when, index)| Agenda::::get(when).get(index as usize).map(|_| when)) - .ok_or(()) - } -}