From f8e5c63b2aefeb0ad003dcbb39926c27713cc0e1 Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Tue, 5 Nov 2024 14:24:37 +0530 Subject: [PATCH 01/16] Adds BlockNumberProvider --- substrate/frame/scheduler/src/lib.rs | 7 +++-- substrate/frame/scheduler/src/mock.rs | 7 +++++ substrate/frame/scheduler/src/tests.rs | 37 ++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/substrate/frame/scheduler/src/lib.rs b/substrate/frame/scheduler/src/lib.rs index 468099010bf9..82904c28af7e 100644 --- a/substrate/frame/scheduler/src/lib.rs +++ b/substrate/frame/scheduler/src/lib.rs @@ -107,7 +107,7 @@ use frame_system::{ use scale_info::TypeInfo; use sp_io::hashing::blake2_256; use sp_runtime::{ - traits::{BadOrigin, Dispatchable, One, Saturating, Zero}, + traits::{BadOrigin, BlockNumberProvider, Dispatchable, One, Saturating, Zero}, BoundedVec, DispatchError, RuntimeDebug, }; @@ -292,6 +292,9 @@ pub mod pallet { /// The preimage provider with which we look up call hashes to get the call. type Preimages: QueryPreimage + StorePreimage; + + /// Provider for the block number. Normally this is the `frame_system` pallet. + type BlockNumberProvider: BlockNumberProvider>; } #[pallet::storage] @@ -889,7 +892,7 @@ impl Pallet { fn resolve_time( when: DispatchTime>, ) -> Result, DispatchError> { - let now = frame_system::Pallet::::block_number(); + let now = T::BlockNumberProvider::current_block_number(); let when = match when { DispatchTime::At(x) => x, diff --git a/substrate/frame/scheduler/src/mock.rs b/substrate/frame/scheduler/src/mock.rs index 8d36ca1c42e3..8c95e796511d 100644 --- a/substrate/frame/scheduler/src/mock.rs +++ b/substrate/frame/scheduler/src/mock.rs @@ -227,6 +227,7 @@ impl Config for Test { type WeightInfo = TestWeightInfo; type OriginPrivilegeCmp = EqualPrivilegeOnly; type Preimages = Preimage; + type BlockNumberProvider = frame_system::Pallet; } pub type LoggerCall = logger::Call; @@ -244,6 +245,12 @@ pub fn run_to_block(n: u64) { } } +pub fn go_to_block(n: u64) { + System::set_block_number(n); + Scheduler::on_initialize(n); + Scheduler::on_finalize(n); +} + pub fn root() -> OriginCaller { system::RawOrigin::Root.into() } diff --git a/substrate/frame/scheduler/src/tests.rs b/substrate/frame/scheduler/src/tests.rs index 3023a370a4b6..5eeef94d70b8 100644 --- a/substrate/frame/scheduler/src/tests.rs +++ b/substrate/frame/scheduler/src/tests.rs @@ -1636,6 +1636,7 @@ fn on_initialize_weight_is_correct() { )); // Will include the named periodic only + System::set_block_number(1); assert_eq!( Scheduler::on_initialize(1), TestWeightInfo::service_agendas_base() + @@ -1648,6 +1649,7 @@ fn on_initialize_weight_is_correct() { assert_eq!(logger::log(), vec![(root(), 2600u32)]); // Will include anon and anon periodic + System::set_block_number(2); assert_eq!( Scheduler::on_initialize(2), TestWeightInfo::service_agendas_base() + @@ -1663,6 +1665,7 @@ fn on_initialize_weight_is_correct() { assert_eq!(logger::log(), vec![(root(), 2600u32), (root(), 69u32), (root(), 42u32)]); // Will include named only + System::set_block_number(3); assert_eq!( Scheduler::on_initialize(3), TestWeightInfo::service_agendas_base() + @@ -1678,6 +1681,7 @@ fn on_initialize_weight_is_correct() { ); // Will contain none + System::set_block_number(4); let actual_weight = Scheduler::on_initialize(4); assert_eq!( actual_weight, @@ -3035,3 +3039,36 @@ fn unavailable_call_is_detected() { assert!(!Preimage::is_requested(&hash)); }); } + +#[test] +fn basic_scheduling_with_skipped_blocks_works() { + new_test_ext().execute_with(|| { + // Call to schedule + let call = + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_parts(10, 0) }); + + // BaseCallFilter should be implemented to accept `Logger::log` runtime call which is + // implemented for `BaseFilter` in the mock runtime + assert!(!::BaseCallFilter::contains(&call)); + + // Schedule call to be executed at the 4th block + assert_ok!(Scheduler::do_schedule( + DispatchTime::At(4), + None, + 127, + root(), + Preimage::bound(call).unwrap() + )); + + // `log` runtime call should not have executed yet + go_to_block(3); + assert!(logger::log().is_empty()); + + go_to_block(6); + // `log` runtime call should have executed at block 4 + assert_eq!(logger::log(), vec![(root(), 42u32)]); + + go_to_block(100); + assert_eq!(logger::log(), vec![(root(), 42u32)]); + }); +} From 455d7650965a1ddc5c797e27ecb539b034f81b10 Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Tue, 5 Nov 2024 14:25:47 +0530 Subject: [PATCH 02/16] Adds a Queue to handle non-sequential block numbers --- .../collectives-westend/src/lib.rs | 4 ++ polkadot/runtime/rococo/src/lib.rs | 3 + polkadot/runtime/westend/src/lib.rs | 3 + substrate/bin/node/runtime/src/lib.rs | 5 ++ substrate/frame/democracy/src/tests.rs | 2 + substrate/frame/referenda/src/mock.rs | 2 + substrate/frame/scheduler/src/lib.rs | 63 +++++++++++++------ substrate/frame/scheduler/src/mock.rs | 1 + substrate/frame/scheduler/src/tests.rs | 5 +- 9 files changed, 66 insertions(+), 22 deletions(-) diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs index 8cb2e42cb31b..4a20c68245ac 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs @@ -604,11 +604,13 @@ parameter_types! { #[cfg(not(feature = "runtime-benchmarks"))] parameter_types! { pub const MaxScheduledPerBlock: u32 = 50; + pub const MaxScheduledBlocks: u32 = 50; } #[cfg(feature = "runtime-benchmarks")] parameter_types! { pub const MaxScheduledPerBlock: u32 = 200; + pub const MaxScheduledBlocks: u32 = 200; } impl pallet_scheduler::Config for Runtime { @@ -622,6 +624,8 @@ impl pallet_scheduler::Config for Runtime { type WeightInfo = weights::pallet_scheduler::WeightInfo; type OriginPrivilegeCmp = EqualOrGreatestRootCmp; type Preimages = Preimage; + type BlockNumberProvider = frame_system::Pallet; + type MaxScheduledBlocks = MaxScheduledBlocks; } parameter_types! { diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 5bcde612cf1b..8106822009bc 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -231,6 +231,7 @@ parameter_types! { pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * BlockWeights::get().max_block; pub const MaxScheduledPerBlock: u32 = 50; + pub const MaxScheduledBlocks: u32 = 50; pub const NoPreimagePostponement: Option = Some(10); } @@ -331,6 +332,8 @@ impl pallet_scheduler::Config for Runtime { type WeightInfo = weights::pallet_scheduler::WeightInfo; type OriginPrivilegeCmp = OriginPrivilegeCmp; type Preimages = Preimage; + type BlockNumberProvider = frame_system::Pallet; + type MaxScheduledBlocks = MaxScheduledBlocks; } parameter_types! { diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 970ef5318994..e35a27343d82 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -231,6 +231,7 @@ parameter_types! { pub MaximumSchedulerWeight: frame_support::weights::Weight = Perbill::from_percent(80) * BlockWeights::get().max_block; pub const MaxScheduledPerBlock: u32 = 50; + pub const MaxScheduledBlocks: u32 = 50; pub const NoPreimagePostponement: Option = Some(10); } @@ -247,6 +248,8 @@ impl pallet_scheduler::Config for Runtime { type WeightInfo = weights::pallet_scheduler::WeightInfo; type OriginPrivilegeCmp = frame_support::traits::EqualPrivilegeOnly; type Preimages = Preimage; + type BlockNumberProvider = frame_system::Pallet; + type MaxScheduledBlocks = MaxScheduledBlocks; } parameter_types! { diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 12e8dc3e5077..3b271625e3c9 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -462,6 +462,11 @@ impl pallet_scheduler::Config for Runtime { type WeightInfo = pallet_scheduler::weights::SubstrateWeight; type OriginPrivilegeCmp = EqualPrivilegeOnly; type Preimages = Preimage; + type BlockNumberProvider = frame_system::Pallet; + #[cfg(feature = "runtime-benchmarks")] + type MaxScheduledBlocks = ConstU32<512>; + #[cfg(not(feature = "runtime-benchmarks"))] + type MaxScheduledBlocks = ConstU32<50>; } impl pallet_glutton::Config for Runtime { diff --git a/substrate/frame/democracy/src/tests.rs b/substrate/frame/democracy/src/tests.rs index 10e5ee75611d..6a09a054452e 100644 --- a/substrate/frame/democracy/src/tests.rs +++ b/substrate/frame/democracy/src/tests.rs @@ -106,6 +106,8 @@ impl pallet_scheduler::Config for Test { type WeightInfo = (); type OriginPrivilegeCmp = EqualPrivilegeOnly; type Preimages = (); + type BlockNumberProvider = frame_system::Pallet; + type MaxScheduledBlocks = ConstU32<100>; } #[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] diff --git a/substrate/frame/referenda/src/mock.rs b/substrate/frame/referenda/src/mock.rs index c96a50af8658..e45353408d76 100644 --- a/substrate/frame/referenda/src/mock.rs +++ b/substrate/frame/referenda/src/mock.rs @@ -81,6 +81,8 @@ impl pallet_scheduler::Config for Test { type WeightInfo = (); type OriginPrivilegeCmp = EqualPrivilegeOnly; type Preimages = Preimage; + type BlockNumberProvider = frame_system::Pallet; + type MaxScheduledBlocks = ConstU32<100>; } #[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] impl pallet_balances::Config for Test { diff --git a/substrate/frame/scheduler/src/lib.rs b/substrate/frame/scheduler/src/lib.rs index 82904c28af7e..b9a7d2c46a42 100644 --- a/substrate/frame/scheduler/src/lib.rs +++ b/substrate/frame/scheduler/src/lib.rs @@ -295,6 +295,10 @@ pub mod pallet { /// Provider for the block number. Normally this is the `frame_system` pallet. type BlockNumberProvider: BlockNumberProvider>; + + /// The maximum number of blocks that can be scheduled. + #[pallet::constant] + type MaxScheduledBlocks: Get; } #[pallet::storage] @@ -328,6 +332,11 @@ pub mod pallet { pub(crate) type Lookup = StorageMap<_, Twox64Concat, TaskName, TaskAddress>>; + /// The queue of block numbers that have scheduled agendas. + #[pallet::storage] + pub(crate) type Queue = + StorageValue<_, BoundedVec, T::MaxScheduledBlocks>, ValueQuery>; + /// Events type. #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] @@ -379,7 +388,8 @@ pub mod pallet { #[pallet::hooks] impl Hooks> for Pallet { /// Execute the scheduled calls - fn on_initialize(now: BlockNumberFor) -> Weight { + fn on_initialize(_do_not_use_local_block_number: BlockNumberFor) -> Weight { + let now = T::BlockNumberProvider::current_block_number(); let mut weight_counter = WeightMeter::with_limit(T::MaximumWeight::get()); Self::service_agendas(&mut weight_counter, now, u32::max_value()); weight_counter.consumed() @@ -929,17 +939,23 @@ impl Pallet { 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)); + let _ = agenda.try_push(Some(what.clone())); agenda.len() as u32 - 1 } else { if let Some(hole_index) = agenda.iter().position(|i| i.is_none()) { - agenda[hole_index] = Some(what); + agenda[hole_index] = Some(what.clone()); hole_index as u32 } else { return Err((DispatchError::Exhausted, what)) } }; Agenda::::insert(when, agenda); + Queue::::mutate(|q| { + if let Err(index) = q.binary_search_by_key(&when, |x| *x) { + q.try_insert(index, when).map_err(|_| (DispatchError::Exhausted, what))?; + } + Ok(()) + })?; Ok(index) } @@ -955,6 +971,11 @@ impl Pallet { Some(_) => {}, None => { Agenda::::remove(when); + Queue::::mutate(|q| { + if let Ok(index) = q.binary_search_by_key(&when, |x| *x) { + q.remove(index); + } + }); }, } } @@ -1160,24 +1181,30 @@ impl Pallet { return } - let mut incomplete_since = now + One::one(); - let mut when = IncompleteSince::::take().unwrap_or(now); - let mut executed = 0; + let queue = Queue::::get(); + let end_index = match queue.binary_search_by_key(&now, |x| *x) { + Ok(end_index) => end_index.saturating_add(1), + Err(end_index) => { + if end_index == 0 { + return; + } + end_index + }, + }; - 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_consume(service_agenda_base_weight) { - if !Self::service_agenda(weight, &mut executed, now, when, u32::max_value()) { - incomplete_since = incomplete_since.min(when); + let mut index = 0; + while index < end_index { + let when = queue[index]; + let mut executed = 0; + if !Self::service_agenda(weight, &mut executed, now, when, max) { + break; } - when.saturating_inc(); - count_down.saturating_dec(); - } - incomplete_since = incomplete_since.min(when); - if incomplete_since <= now { - IncompleteSince::::put(incomplete_since); + index.saturating_inc(); } + + Queue::::mutate(|queue| { + queue.drain(0..index); + }); } /// Returns `true` if the agenda was fully completed, `false` if it should be revisited at a diff --git a/substrate/frame/scheduler/src/mock.rs b/substrate/frame/scheduler/src/mock.rs index 8c95e796511d..73441d44f308 100644 --- a/substrate/frame/scheduler/src/mock.rs +++ b/substrate/frame/scheduler/src/mock.rs @@ -228,6 +228,7 @@ impl Config for Test { type OriginPrivilegeCmp = EqualPrivilegeOnly; type Preimages = Preimage; type BlockNumberProvider = frame_system::Pallet; + type MaxScheduledBlocks = ConstU32<20>; } pub type LoggerCall = logger::Call; diff --git a/substrate/frame/scheduler/src/tests.rs b/substrate/frame/scheduler/src/tests.rs index 5eeef94d70b8..bb63d105d15f 100644 --- a/substrate/frame/scheduler/src/tests.rs +++ b/substrate/frame/scheduler/src/tests.rs @@ -1683,10 +1683,7 @@ fn on_initialize_weight_is_correct() { // Will contain none System::set_block_number(4); let actual_weight = Scheduler::on_initialize(4); - assert_eq!( - actual_weight, - TestWeightInfo::service_agendas_base() + TestWeightInfo::service_agenda_base(0) - ); + assert_eq!(actual_weight, TestWeightInfo::service_agendas_base()); }); } From 762c8162a3624b5132f2e852e0bcc556ce3cf5e8 Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Mon, 11 Nov 2024 16:20:38 +0530 Subject: [PATCH 03/16] Removes constraint --- substrate/frame/scheduler/src/lib.rs | 18 +++++++----------- substrate/frame/scheduler/src/migration.rs | 1 - substrate/frame/scheduler/src/tests.rs | 3 --- substrate/primitives/runtime/src/traits/mod.rs | 3 ++- 4 files changed, 9 insertions(+), 16 deletions(-) diff --git a/substrate/frame/scheduler/src/lib.rs b/substrate/frame/scheduler/src/lib.rs index b9a7d2c46a42..86ab00894f79 100644 --- a/substrate/frame/scheduler/src/lib.rs +++ b/substrate/frame/scheduler/src/lib.rs @@ -100,10 +100,7 @@ use frame_support::{ }, weights::{Weight, WeightMeter}, }; -use frame_system::{ - pallet_prelude::BlockNumberFor, - {self as system}, -}; +use frame_system::{self as system}; use scale_info::TypeInfo; use sp_io::hashing::blake2_256; use sp_runtime::{ @@ -125,6 +122,8 @@ pub type CallOrHashOf = pub type BoundedCallOf = Bounded<::RuntimeCall, ::Hashing>; +pub type BlockNumberFor = <::BlockNumberProvider as BlockNumberProvider>::BlockNumber; + /// The configuration of the retry mechanism for a given task along with its current state. #[derive(Clone, Copy, RuntimeDebug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)] pub struct RetryConfig { @@ -230,7 +229,7 @@ impl MarginalWeightInfo for T {} pub mod pallet { use super::*; use frame_support::{dispatch::PostDispatchInfo, pallet_prelude::*}; - use frame_system::pallet_prelude::*; + use frame_system::pallet_prelude::{OriginFor, BlockNumberFor as SystemBlockNumberFor}; /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(4); @@ -294,16 +293,13 @@ pub mod pallet { type Preimages: QueryPreimage + StorePreimage; /// Provider for the block number. Normally this is the `frame_system` pallet. - type BlockNumberProvider: BlockNumberProvider>; + type BlockNumberProvider: BlockNumberProvider; /// The maximum number of blocks that can be scheduled. #[pallet::constant] type MaxScheduledBlocks: Get; } - #[pallet::storage] - pub type IncompleteSince = StorageValue<_, BlockNumberFor>; - /// Items to be executed, indexed by the block number that they should be executed on. #[pallet::storage] pub type Agenda = StorageMap< @@ -386,9 +382,9 @@ pub mod pallet { } #[pallet::hooks] - impl Hooks> for Pallet { + impl Hooks> for Pallet { /// Execute the scheduled calls - fn on_initialize(_do_not_use_local_block_number: BlockNumberFor) -> Weight { + fn on_initialize(_do_not_use_local_block_number: SystemBlockNumberFor) -> Weight { let now = T::BlockNumberProvider::current_block_number(); let mut weight_counter = WeightMeter::with_limit(T::MaximumWeight::get()); Self::service_agendas(&mut weight_counter, now, u32::max_value()); diff --git a/substrate/frame/scheduler/src/migration.rs b/substrate/frame/scheduler/src/migration.rs index a304689a120c..f3d04f215ee0 100644 --- a/substrate/frame/scheduler/src/migration.rs +++ b/substrate/frame/scheduler/src/migration.rs @@ -19,7 +19,6 @@ use super::*; use frame_support::traits::OnRuntimeUpgrade; -use frame_system::pallet_prelude::BlockNumberFor; #[cfg(feature = "try-runtime")] use sp_runtime::TryRuntimeError; diff --git a/substrate/frame/scheduler/src/tests.rs b/substrate/frame/scheduler/src/tests.rs index bb63d105d15f..cd47126dd0b2 100644 --- a/substrate/frame/scheduler/src/tests.rs +++ b/substrate/frame/scheduler/src/tests.rs @@ -1645,7 +1645,6 @@ fn on_initialize_weight_is_correct() { TestWeightInfo::execute_dispatch_unsigned() + call_weight + Weight::from_parts(4, 0) ); - assert_eq!(IncompleteSince::::get(), None); assert_eq!(logger::log(), vec![(root(), 2600u32)]); // Will include anon and anon periodic @@ -1661,7 +1660,6 @@ fn on_initialize_weight_is_correct() { TestWeightInfo::execute_dispatch_unsigned() + call_weight + Weight::from_parts(2, 0) ); - assert_eq!(IncompleteSince::::get(), None); assert_eq!(logger::log(), vec![(root(), 2600u32), (root(), 69u32), (root(), 42u32)]); // Will include named only @@ -1674,7 +1672,6 @@ fn on_initialize_weight_is_correct() { TestWeightInfo::execute_dispatch_unsigned() + call_weight + Weight::from_parts(1, 0) ); - assert_eq!(IncompleteSince::::get(), None); assert_eq!( logger::log(), vec![(root(), 2600u32), (root(), 69u32), (root(), 42u32), (root(), 3u32)] diff --git a/substrate/primitives/runtime/src/traits/mod.rs b/substrate/primitives/runtime/src/traits/mod.rs index e6906cdb3877..74b948fc5d18 100644 --- a/substrate/primitives/runtime/src/traits/mod.rs +++ b/substrate/primitives/runtime/src/traits/mod.rs @@ -2348,7 +2348,8 @@ pub trait BlockNumberProvider { + TypeInfo + Debug + MaxEncodedLen - + Copy; + + Copy + + EncodeLike; /// Returns the current block number. /// From 86701cfed637a9cf1236ec666940bfc018f0565e Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Wed, 13 Nov 2024 09:52:13 +0530 Subject: [PATCH 04/16] Updates benchmarks --- substrate/frame/scheduler/src/benchmarking.rs | 74 ++++++++++++++----- 1 file changed, 54 insertions(+), 20 deletions(-) diff --git a/substrate/frame/scheduler/src/benchmarking.rs b/substrate/frame/scheduler/src/benchmarking.rs index d0a14fc73d64..2bbdd04e1cb4 100644 --- a/substrate/frame/scheduler/src/benchmarking.rs +++ b/substrate/frame/scheduler/src/benchmarking.rs @@ -22,17 +22,20 @@ use alloc::vec; use frame_benchmarking::v1::{account, benchmarks, BenchmarkError}; use frame_support::{ ensure, + testing_prelude::*, traits::{schedule::Priority, BoundedInline}, weights::WeightMeter, }; -use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin}; +use frame_system::RawOrigin; use crate::Pallet as Scheduler; use frame_system::{Call as SystemCall, EventRecord}; const SEED: u32 = 0; -const BLOCK_NUMBER: u32 = 2; +fn block_number() -> u32 { + T::MaxScheduledBlocks::get() +} type SystemOrigin = ::RuntimeOrigin; @@ -44,6 +47,15 @@ fn assert_last_event(generic_event: ::RuntimeEvent) { assert_eq!(event, &system_event); } +fn fill_queue(n: u32) { + let mut vec = Vec::>::new(); + for i in 0..n { + vec.push(i.into()); + } + let bounded_vec = BoundedVec::try_from(vec).unwrap(); + Queue::::put::>(bounded_vec); +} + /// Add `n` items to the schedule. /// /// For `resolved`: @@ -52,7 +64,7 @@ fn assert_last_event(generic_event: ::RuntimeEvent) { /// - `Some(true)`: hash resolves into call if possible, plain call otherwise /// - `Some(false)`: plain call fn fill_schedule( - when: frame_system::pallet_prelude::BlockNumberFor, + when: BlockNumberFor, n: u32, ) -> Result<(), &'static str> { let t = DispatchTime::At(when); @@ -137,17 +149,19 @@ fn make_origin(signed: bool) -> ::PalletsOrigin { benchmarks! { // `service_agendas` when no work is done. service_agendas_base { - let now = BlockNumberFor::::from(BLOCK_NUMBER); - IncompleteSince::::put(now - One::one()); + let now = BlockNumberFor::::from(block_number::()); + Queue::::put::>(bounded_vec![now + One::one()]); }: { Scheduler::::service_agendas(&mut WeightMeter::new(), now, 0); } verify { - assert_eq!(IncompleteSince::::get(), Some(now - One::one())); + let expected: BoundedVec, T::MaxScheduledBlocks> = + bounded_vec![now + One::one()]; + assert_eq!(Queue::::get(), expected); } // `service_agenda` when no work is done. service_agenda_base { - let now = BLOCK_NUMBER.into(); + let now = block_number::().into(); let s in 0 .. T::MaxScheduledPerBlock::get(); fill_schedule::(now, s)?; let mut executed = 0; @@ -160,7 +174,7 @@ benchmarks! { // `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 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 = WeightMeter::with_limit(Weight::zero()); @@ -178,7 +192,7 @@ benchmarks! { }] service_task_fetched { let s in (BoundedInline::bound() as u32) .. (T::Preimages::MAX_LENGTH as u32); - let now = BLOCK_NUMBER.into(); + 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 = WeightMeter::with_limit(Weight::zero()); @@ -190,7 +204,7 @@ benchmarks! { // `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 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 = WeightMeter::with_limit(Weight::zero()); @@ -202,7 +216,7 @@ benchmarks! { // `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 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 = WeightMeter::with_limit(Weight::zero()); @@ -235,26 +249,32 @@ benchmarks! { schedule { let s in 0 .. (T::MaxScheduledPerBlock::get() - 1); - let when = BLOCK_NUMBER.into(); + let when = block_number::().into(); let periodic = Some((BlockNumberFor::::one(), 100)); let priority = 0; // Essentially a no-op call. let call = Box::new(SystemCall::set_storage { items: vec![] }.into()); fill_schedule::(when, s)?; + fill_queue::(T::MaxScheduledBlocks::get() - 1); }: _(RawOrigin::Root, when, periodic, priority, call) verify { ensure!( Agenda::::get(when).len() == (s + 1) as usize, "didn't add to schedule" ); + ensure!( + Queue::::get().len() == T::MaxScheduledBlocks::get() as usize, + "didn't add to queue" + ); } cancel { let s in 1 .. T::MaxScheduledPerBlock::get(); - let when = BLOCK_NUMBER.into(); + let when = (block_number::() - 1).into(); fill_schedule::(when, s)?; + fill_queue::(T::MaxScheduledBlocks::get()); assert_eq!(Agenda::::get(when).len(), s as usize); let schedule_origin = T::ScheduleOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; @@ -273,31 +293,41 @@ benchmarks! { s > 1 || Agenda::::get(when).len() == 0, "remove from schedule if only 1 task scheduled for `when`" ); + ensure!( + s > 1 || Queue::::get().len() == T::MaxScheduledBlocks::get() as usize - 1, + "didn't remove from queue" + ); } schedule_named { let s in 0 .. (T::MaxScheduledPerBlock::get() - 1); let id = u32_to_name(s); - let when = BLOCK_NUMBER.into(); + let when = block_number::().into(); let periodic = Some((BlockNumberFor::::one(), 100)); let priority = 0; // Essentially a no-op call. let call = Box::new(SystemCall::set_storage { items: vec![] }.into()); fill_schedule::(when, s)?; + fill_queue::(T::MaxScheduledBlocks::get() - 1); }: _(RawOrigin::Root, id, when, periodic, priority, call) verify { ensure!( Agenda::::get(when).len() == (s + 1) as usize, "didn't add to schedule" ); + ensure!( + Queue::::get().len() == T::MaxScheduledBlocks::get() as usize, + "didn't add to queue" + ); } cancel_named { let s in 1 .. T::MaxScheduledPerBlock::get(); - let when = BLOCK_NUMBER.into(); + let when = (block_number::() - 1).into(); fill_schedule::(when, s)?; + fill_queue::(T::MaxScheduledBlocks::get()); }: _(RawOrigin::Root, u32_to_name(0)) verify { ensure!( @@ -313,11 +343,15 @@ benchmarks! { s > 1 || Agenda::::get(when).len() == 0, "remove from schedule if only 1 task scheduled for `when`" ); + ensure!( + s > 1 || Queue::::get().len() == T::MaxScheduledBlocks::get() as usize - 1, + "didn't remove from queue" + ); } schedule_retry { let s in 1 .. T::MaxScheduledPerBlock::get(); - let when = BLOCK_NUMBER.into(); + let when = block_number::().into(); fill_schedule::(when, s)?; let name = u32_to_name(s - 1); @@ -341,7 +375,7 @@ benchmarks! { set_retry { let s = T::MaxScheduledPerBlock::get(); - let when = BLOCK_NUMBER.into(); + let when = block_number::().into(); fill_schedule::(when, s)?; let name = u32_to_name(s - 1); @@ -361,7 +395,7 @@ benchmarks! { set_retry_named { let s = T::MaxScheduledPerBlock::get(); - let when = BLOCK_NUMBER.into(); + let when = block_number::().into(); fill_schedule::(when, s)?; let name = u32_to_name(s - 1); @@ -381,7 +415,7 @@ benchmarks! { cancel_retry { let s = T::MaxScheduledPerBlock::get(); - let when = BLOCK_NUMBER.into(); + let when = block_number::().into(); fill_schedule::(when, s)?; let name = u32_to_name(s - 1); @@ -399,7 +433,7 @@ benchmarks! { cancel_retry_named { let s = T::MaxScheduledPerBlock::get(); - let when = BLOCK_NUMBER.into(); + let when = block_number::().into(); fill_schedule::(when, s)?; let name = u32_to_name(s - 1); From 27d1fa1473b6a51ebc74f116d7abfcb875b1e1d7 Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Thu, 14 Nov 2024 10:03:43 +0530 Subject: [PATCH 05/16] Adds migration --- substrate/frame/scheduler/src/migration.rs | 72 ++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/substrate/frame/scheduler/src/migration.rs b/substrate/frame/scheduler/src/migration.rs index f3d04f215ee0..2c3e840ec44e 100644 --- a/substrate/frame/scheduler/src/migration.rs +++ b/substrate/frame/scheduler/src/migration.rs @@ -560,3 +560,75 @@ mod test { system::RawOrigin::Signed(i).into() } } + +pub mod v5 { + use super::*; + use frame_support::{migrations::VersionedMigration, traits::UncheckedOnRuntimeUpgrade, pallet_prelude::*}; + + #[frame_support::storage_alias] + pub(crate) type IncompleteSince = StorageValue, BlockNumberFor>; + + pub struct VersionUncheckedMigrateV4ToV5(core::marker::PhantomData); + impl UncheckedOnRuntimeUpgrade for VersionUncheckedMigrateV4ToV5 { + fn on_runtime_upgrade() -> Weight { + IncompleteSince::::kill(); + let mut weight = T::DbWeight::get().writes(1); + + let mut queue = vec![]; + Agenda::::iter().for_each(|(k, _)| { + queue.push(k); + weight.saturating_accrue(T::DbWeight::get().reads(1)); + }); + queue.sort_unstable(); + queue.dedup(); + + let queue = BoundedVec::, T::MaxScheduledBlocks>::try_from(queue).unwrap(); + Queue::::put(queue); + weight.saturating_accrue(T::DbWeight::get().writes(1)); + + weight + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_state: Vec) -> Result<(), TryRuntimeError> { + frame_support::ensure!( + IncompleteSince::::get().is_none(), + "IncompleteSince is not empty after the migration" + ); + Ok(()) + } + } + + pub type MigrateV4ToV5 = VersionedMigration< + 4, + 5, + VersionUncheckedMigrateV4ToV5, + Pallet, + ::DbWeight, + >; +} + +#[cfg(test)] +pub mod test { + use super::*; + use crate::mock::*; + use crate::migration::v5::IncompleteSince; + use frame_support::testing_prelude::*; + + #[test] + fn migration_v4_to_v5_works() { + new_test_ext().execute_with(|| { + StorageVersion::new(4).put::(); + IncompleteSince::::put(1); + Agenda::::insert::>, ::MaxScheduledPerBlock>>(2, bounded_vec![None]); + Agenda::::insert::>, ::MaxScheduledPerBlock>>(3, bounded_vec![None]); + + v5::MigrateV4ToV5::::on_runtime_upgrade(); + + assert_eq!(StorageVersion::get::(), 5); + assert_eq!(IncompleteSince::::get(), None); + let queue = BoundedVec::, ::MaxScheduledBlocks>::try_from(vec![2, 3]).unwrap(); + assert_eq!(Queue::::get(), queue); + }); + } +} From c79900cf525dcabef94dba11d6a6d5393d6ece2b Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Thu, 14 Nov 2024 10:04:25 +0530 Subject: [PATCH 06/16] FMT --- substrate/frame/scheduler/src/benchmarking.rs | 5 +--- substrate/frame/scheduler/src/lib.rs | 5 ++-- substrate/frame/scheduler/src/migration.rs | 28 +++++++++++++------ 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/substrate/frame/scheduler/src/benchmarking.rs b/substrate/frame/scheduler/src/benchmarking.rs index 2bbdd04e1cb4..20e22774d31e 100644 --- a/substrate/frame/scheduler/src/benchmarking.rs +++ b/substrate/frame/scheduler/src/benchmarking.rs @@ -63,10 +63,7 @@ fn fill_queue(n: u32) { /// - `None`: aborted (hash without preimage) /// - `Some(true)`: hash resolves into call if possible, plain call otherwise /// - `Some(false)`: plain call -fn fill_schedule( - when: BlockNumberFor, - n: u32, -) -> Result<(), &'static str> { +fn fill_schedule(when: BlockNumberFor, n: u32) -> Result<(), &'static str> { let t = DispatchTime::At(when); let origin: ::PalletsOrigin = frame_system::RawOrigin::Root.into(); for i in 0..n { diff --git a/substrate/frame/scheduler/src/lib.rs b/substrate/frame/scheduler/src/lib.rs index 86ab00894f79..cc4420d4a65d 100644 --- a/substrate/frame/scheduler/src/lib.rs +++ b/substrate/frame/scheduler/src/lib.rs @@ -122,7 +122,8 @@ pub type CallOrHashOf = pub type BoundedCallOf = Bounded<::RuntimeCall, ::Hashing>; -pub type BlockNumberFor = <::BlockNumberProvider as BlockNumberProvider>::BlockNumber; +pub type BlockNumberFor = + <::BlockNumberProvider as BlockNumberProvider>::BlockNumber; /// The configuration of the retry mechanism for a given task along with its current state. #[derive(Clone, Copy, RuntimeDebug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)] @@ -229,7 +230,7 @@ impl MarginalWeightInfo for T {} pub mod pallet { use super::*; use frame_support::{dispatch::PostDispatchInfo, pallet_prelude::*}; - use frame_system::pallet_prelude::{OriginFor, BlockNumberFor as SystemBlockNumberFor}; + use frame_system::pallet_prelude::{BlockNumberFor as SystemBlockNumberFor, OriginFor}; /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(4); diff --git a/substrate/frame/scheduler/src/migration.rs b/substrate/frame/scheduler/src/migration.rs index 2c3e840ec44e..acc84adf666c 100644 --- a/substrate/frame/scheduler/src/migration.rs +++ b/substrate/frame/scheduler/src/migration.rs @@ -563,7 +563,9 @@ mod test { pub mod v5 { use super::*; - use frame_support::{migrations::VersionedMigration, traits::UncheckedOnRuntimeUpgrade, pallet_prelude::*}; + use frame_support::{ + migrations::VersionedMigration, pallet_prelude::*, traits::UncheckedOnRuntimeUpgrade, + }; #[frame_support::storage_alias] pub(crate) type IncompleteSince = StorageValue, BlockNumberFor>; @@ -573,7 +575,7 @@ pub mod v5 { fn on_runtime_upgrade() -> Weight { IncompleteSince::::kill(); let mut weight = T::DbWeight::get().writes(1); - + let mut queue = vec![]; Agenda::::iter().for_each(|(k, _)| { queue.push(k); @@ -582,7 +584,8 @@ pub mod v5 { queue.sort_unstable(); queue.dedup(); - let queue = BoundedVec::, T::MaxScheduledBlocks>::try_from(queue).unwrap(); + let queue = + BoundedVec::, T::MaxScheduledBlocks>::try_from(queue).unwrap(); Queue::::put(queue); weight.saturating_accrue(T::DbWeight::get().writes(1)); @@ -611,8 +614,7 @@ pub mod v5 { #[cfg(test)] pub mod test { use super::*; - use crate::mock::*; - use crate::migration::v5::IncompleteSince; + use crate::{migration::v5::IncompleteSince, mock::*}; use frame_support::testing_prelude::*; #[test] @@ -620,14 +622,24 @@ pub mod test { new_test_ext().execute_with(|| { StorageVersion::new(4).put::(); IncompleteSince::::put(1); - Agenda::::insert::>, ::MaxScheduledPerBlock>>(2, bounded_vec![None]); - Agenda::::insert::>, ::MaxScheduledPerBlock>>(3, bounded_vec![None]); + Agenda::::insert::< + u64, + BoundedVec>, ::MaxScheduledPerBlock>, + >(2, bounded_vec![None]); + Agenda::::insert::< + u64, + BoundedVec>, ::MaxScheduledPerBlock>, + >(3, bounded_vec![None]); v5::MigrateV4ToV5::::on_runtime_upgrade(); assert_eq!(StorageVersion::get::(), 5); assert_eq!(IncompleteSince::::get(), None); - let queue = BoundedVec::, ::MaxScheduledBlocks>::try_from(vec![2, 3]).unwrap(); + let queue = + BoundedVec::, ::MaxScheduledBlocks>::try_from( + vec![2, 3], + ) + .unwrap(); assert_eq!(Queue::::get(), queue); }); } From 622805d69fc9088de6a92144e83d3a77f29f40e2 Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Thu, 14 Nov 2024 10:09:44 +0530 Subject: [PATCH 07/16] Adds PrDoc --- prdoc/pr_6362.prdoc | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 prdoc/pr_6362.prdoc diff --git a/prdoc/pr_6362.prdoc b/prdoc/pr_6362.prdoc new file mode 100644 index 000000000000..9ddee2037479 --- /dev/null +++ b/prdoc/pr_6362.prdoc @@ -0,0 +1,23 @@ +title: Update Scheduler to Support Relay Chain Block Number Provider + +doc: + - audience: Runtime Dev + description: | + This PR adds the ability for the Scheduler pallet to specify its source of the block number. + This is needed for the scheduler pallet to work on a parachain which does not produce blocks + on a regular schedule, thus can use the relay chain as a block provider. Because blocks are + not produced regularly, we cannot make the assumption that the block number increases + monotonically, and thus have a new logic to handle multiple blocks with valid agenda passed + between them. + + This change is backwards compatible: + 1. If the `BlockNumberProvider` continues to use the system pallet's block number + 2. When a pallet deployed on the relay chain is moved to a parachain, but still uses the + relay chain's block number + + However, we would need migrations if the deployed pallets are upgraded on an existing parachain, + and the `BlockNumberProvider` uses the relay chain block number. + +crates: + - name: pallet-scheduler + bump: major From abbd346bbe1e6405012a38f98ddff1e350f113cd Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Thu, 14 Nov 2024 10:20:19 +0530 Subject: [PATCH 08/16] Updates PrDoc --- prdoc/pr_6362.prdoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/prdoc/pr_6362.prdoc b/prdoc/pr_6362.prdoc index 9ddee2037479..57586d1423d4 100644 --- a/prdoc/pr_6362.prdoc +++ b/prdoc/pr_6362.prdoc @@ -7,10 +7,10 @@ doc: This is needed for the scheduler pallet to work on a parachain which does not produce blocks on a regular schedule, thus can use the relay chain as a block provider. Because blocks are not produced regularly, we cannot make the assumption that the block number increases - monotonically, and thus have a new logic to handle multiple blocks with valid agenda passed - between them. + monotonically, and thus have a new logic via a `Queue` to handle multiple blocks with valid + agenda passed between them. - This change is backwards compatible: + This change only needs a migration for the `Queue`: 1. If the `BlockNumberProvider` continues to use the system pallet's block number 2. When a pallet deployed on the relay chain is moved to a parachain, but still uses the relay chain's block number From 813e5965178dcff3f5ba093553ae80776e5e2e89 Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Fri, 15 Nov 2024 15:16:47 +0530 Subject: [PATCH 09/16] Fixes --- substrate/frame/scheduler/src/benchmarking.rs | 2 +- substrate/frame/scheduler/src/migration.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/substrate/frame/scheduler/src/benchmarking.rs b/substrate/frame/scheduler/src/benchmarking.rs index 20e22774d31e..0136d770285a 100644 --- a/substrate/frame/scheduler/src/benchmarking.rs +++ b/substrate/frame/scheduler/src/benchmarking.rs @@ -22,11 +22,11 @@ use alloc::vec; use frame_benchmarking::v1::{account, benchmarks, BenchmarkError}; use frame_support::{ ensure, - testing_prelude::*, traits::{schedule::Priority, BoundedInline}, weights::WeightMeter, }; use frame_system::RawOrigin; +use sp_runtime::bounded_vec; use crate::Pallet as Scheduler; use frame_system::{Call as SystemCall, EventRecord}; diff --git a/substrate/frame/scheduler/src/migration.rs b/substrate/frame/scheduler/src/migration.rs index acc84adf666c..3879206d8330 100644 --- a/substrate/frame/scheduler/src/migration.rs +++ b/substrate/frame/scheduler/src/migration.rs @@ -616,6 +616,7 @@ pub mod test { use super::*; use crate::{migration::v5::IncompleteSince, mock::*}; use frame_support::testing_prelude::*; + use scale_info::prelude::vec; #[test] fn migration_v4_to_v5_works() { From 8c40f24bafdaffd6d2bb628e708e78af78012e83 Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Fri, 15 Nov 2024 15:39:22 +0530 Subject: [PATCH 10/16] Adds MaxStaleTaskAge --- .../collectives-westend/src/lib.rs | 2 ++ polkadot/runtime/rococo/src/lib.rs | 2 ++ polkadot/runtime/westend/src/lib.rs | 2 ++ substrate/bin/node/runtime/src/lib.rs | 1 + substrate/frame/democracy/src/tests.rs | 1 + substrate/frame/referenda/src/mock.rs | 1 + substrate/frame/scheduler/src/lib.rs | 14 ++++++++--- substrate/frame/scheduler/src/mock.rs | 5 +++- substrate/frame/scheduler/src/tests.rs | 23 +++++++++++++++++++ 9 files changed, 47 insertions(+), 4 deletions(-) diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs index f135c1463276..62927807afb8 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs @@ -611,6 +611,7 @@ parameter_types! { parameter_types! { pub const MaxScheduledPerBlock: u32 = 200; pub const MaxScheduledBlocks: u32 = 200; + pub const MaxStaleTaskAge: u64 = 10; } impl pallet_scheduler::Config for Runtime { @@ -626,6 +627,7 @@ impl pallet_scheduler::Config for Runtime { type Preimages = Preimage; type BlockNumberProvider = frame_system::Pallet; type MaxScheduledBlocks = MaxScheduledBlocks; + type MaxStaleTaskAge = MaxStaleTaskAge; } parameter_types! { diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index f2db0e4334aa..840408979c5f 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -232,6 +232,7 @@ parameter_types! { BlockWeights::get().max_block; pub const MaxScheduledPerBlock: u32 = 50; pub const MaxScheduledBlocks: u32 = 50; + pub const MaxStaleTaskAge: u64 = 10; pub const NoPreimagePostponement: Option = Some(10); } @@ -334,6 +335,7 @@ impl pallet_scheduler::Config for Runtime { type Preimages = Preimage; type BlockNumberProvider = frame_system::Pallet; type MaxScheduledBlocks = MaxScheduledBlocks; + type MaxStaleTaskAge = MaxStaleTaskAge; } parameter_types! { diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 86b612fbdc61..c05ce1228d41 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -233,6 +233,7 @@ parameter_types! { BlockWeights::get().max_block; pub const MaxScheduledPerBlock: u32 = 50; pub const MaxScheduledBlocks: u32 = 50; + pub const MaxStaleTaskAge: u64 = 10; pub const NoPreimagePostponement: Option = Some(10); } @@ -251,6 +252,7 @@ impl pallet_scheduler::Config for Runtime { type Preimages = Preimage; type BlockNumberProvider = frame_system::Pallet; type MaxScheduledBlocks = MaxScheduledBlocks; + type MaxStaleTaskAge = MaxStaleTaskAge; } parameter_types! { diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index e0c7b051826f..d49d91563762 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -504,6 +504,7 @@ impl pallet_scheduler::Config for Runtime { type MaxScheduledBlocks = ConstU32<512>; #[cfg(not(feature = "runtime-benchmarks"))] type MaxScheduledBlocks = ConstU32<50>; + type MaxStaleTaskAge = ConstU64<10>; } impl pallet_glutton::Config for Runtime { diff --git a/substrate/frame/democracy/src/tests.rs b/substrate/frame/democracy/src/tests.rs index 6a09a054452e..3d88f49a4138 100644 --- a/substrate/frame/democracy/src/tests.rs +++ b/substrate/frame/democracy/src/tests.rs @@ -108,6 +108,7 @@ impl pallet_scheduler::Config for Test { type Preimages = (); type BlockNumberProvider = frame_system::Pallet; type MaxScheduledBlocks = ConstU32<100>; + type MaxStaleTaskAge = ConstU64<10>; } #[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] diff --git a/substrate/frame/referenda/src/mock.rs b/substrate/frame/referenda/src/mock.rs index e45353408d76..c6ef49fdb146 100644 --- a/substrate/frame/referenda/src/mock.rs +++ b/substrate/frame/referenda/src/mock.rs @@ -83,6 +83,7 @@ impl pallet_scheduler::Config for Test { type Preimages = Preimage; type BlockNumberProvider = frame_system::Pallet; type MaxScheduledBlocks = ConstU32<100>; + type MaxStaleTaskAge = ConstU64<10>; } #[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] impl pallet_balances::Config for Test { diff --git a/substrate/frame/scheduler/src/lib.rs b/substrate/frame/scheduler/src/lib.rs index cc4420d4a65d..ad5c522b5945 100644 --- a/substrate/frame/scheduler/src/lib.rs +++ b/substrate/frame/scheduler/src/lib.rs @@ -299,6 +299,10 @@ pub mod pallet { /// The maximum number of blocks that can be scheduled. #[pallet::constant] type MaxScheduledBlocks: Get; + + /// The maximum number of blocks that a task can be stale for. + #[pallet::constant] + type MaxStaleTaskAge: Get>; } /// Items to be executed, indexed by the block number that they should be executed on. @@ -1192,9 +1196,13 @@ impl Pallet { let mut index = 0; while index < end_index { let when = queue[index]; - let mut executed = 0; - if !Self::service_agenda(weight, &mut executed, now, when, max) { - break; + if when < now.saturating_sub(T::MaxStaleTaskAge::get()) { + Agenda::::remove(when); + } else { + let mut executed = 0; + if !Self::service_agenda(weight, &mut executed, now, when, max) { + break; + } } index.saturating_inc(); } diff --git a/substrate/frame/scheduler/src/mock.rs b/substrate/frame/scheduler/src/mock.rs index 73441d44f308..18357c0c1498 100644 --- a/substrate/frame/scheduler/src/mock.rs +++ b/substrate/frame/scheduler/src/mock.rs @@ -22,7 +22,9 @@ use super::*; use crate as scheduler; use frame_support::{ derive_impl, ord_parameter_types, parameter_types, - traits::{ConstU32, Contains, EitherOfDiverse, EqualPrivilegeOnly, OnFinalize, OnInitialize}, + traits::{ + ConstU32, ConstU64, Contains, EitherOfDiverse, EqualPrivilegeOnly, OnFinalize, OnInitialize, + }, }; use frame_system::{EnsureRoot, EnsureSignedBy}; use sp_runtime::{BuildStorage, Perbill}; @@ -229,6 +231,7 @@ impl Config for Test { type Preimages = Preimage; type BlockNumberProvider = frame_system::Pallet; type MaxScheduledBlocks = ConstU32<20>; + type MaxStaleTaskAge = ConstU64<10>; } pub type LoggerCall = logger::Call; diff --git a/substrate/frame/scheduler/src/tests.rs b/substrate/frame/scheduler/src/tests.rs index cd47126dd0b2..fa77dcbdeeb6 100644 --- a/substrate/frame/scheduler/src/tests.rs +++ b/substrate/frame/scheduler/src/tests.rs @@ -3066,3 +3066,26 @@ fn basic_scheduling_with_skipped_blocks_works() { assert_eq!(logger::log(), vec![(root(), 42u32)]); }); } + +#[test] +fn stale_task_is_removed() { + new_test_ext().execute_with(|| { + let call = + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_parts(10, 0) }); + + // Schedule call to be executed at the 4th block + assert_ok!(Scheduler::do_schedule( + DispatchTime::At(4), + None, + 127, + root(), + Preimage::bound(call).unwrap() + )); + assert!(Agenda::::get(4).len() == 1); + assert!(Queue::::get().contains(&4)); + + go_to_block(20); + assert!(Agenda::::get(4).is_empty()); + assert!(!Queue::::get().contains(&4)); + }) +} From b905628213cd7d3581d0c520e4764d3928923704 Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Fri, 15 Nov 2024 16:01:07 +0530 Subject: [PATCH 11/16] Fixes --- substrate/frame/scheduler/src/benchmarking.rs | 5 +++-- substrate/frame/scheduler/src/migration.rs | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/substrate/frame/scheduler/src/benchmarking.rs b/substrate/frame/scheduler/src/benchmarking.rs index 0136d770285a..8cb622f8e59d 100644 --- a/substrate/frame/scheduler/src/benchmarking.rs +++ b/substrate/frame/scheduler/src/benchmarking.rs @@ -22,6 +22,7 @@ use alloc::vec; use frame_benchmarking::v1::{account, benchmarks, BenchmarkError}; use frame_support::{ ensure, + storage::bounded_vec::BoundedVec, traits::{schedule::Priority, BoundedInline}, weights::WeightMeter, }; @@ -147,12 +148,12 @@ benchmarks! { // `service_agendas` when no work is done. service_agendas_base { let now = BlockNumberFor::::from(block_number::()); - Queue::::put::>(bounded_vec![now + One::one()]); + Queue::::put::>(BoundedVec::try_from(vec![now + One::one()]).unwrap()); }: { Scheduler::::service_agendas(&mut WeightMeter::new(), now, 0); } verify { let expected: BoundedVec, T::MaxScheduledBlocks> = - bounded_vec![now + One::one()]; + BoundedVec::try_from(vec![now + One::one()]).unwrap(); assert_eq!(Queue::::get(), expected); } diff --git a/substrate/frame/scheduler/src/migration.rs b/substrate/frame/scheduler/src/migration.rs index 3879206d8330..09d915b8d423 100644 --- a/substrate/frame/scheduler/src/migration.rs +++ b/substrate/frame/scheduler/src/migration.rs @@ -615,8 +615,8 @@ pub mod v5 { pub mod test { use super::*; use crate::{migration::v5::IncompleteSince, mock::*}; + use alloc::vec; use frame_support::testing_prelude::*; - use scale_info::prelude::vec; #[test] fn migration_v4_to_v5_works() { From 2ea271adf830d96570fc37f6e95a4412a602681f Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Fri, 15 Nov 2024 16:08:09 +0530 Subject: [PATCH 12/16] Minor fix --- substrate/frame/scheduler/src/migration.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/substrate/frame/scheduler/src/migration.rs b/substrate/frame/scheduler/src/migration.rs index 09d915b8d423..ae1d351874ac 100644 --- a/substrate/frame/scheduler/src/migration.rs +++ b/substrate/frame/scheduler/src/migration.rs @@ -563,6 +563,7 @@ mod test { pub mod v5 { use super::*; + use alloc::vec; use frame_support::{ migrations::VersionedMigration, pallet_prelude::*, traits::UncheckedOnRuntimeUpgrade, }; From 9a73c23e1521e8150011ce7309d9f63058912758 Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Fri, 15 Nov 2024 16:20:42 +0530 Subject: [PATCH 13/16] Minor fix --- .../runtimes/collectives/collectives-westend/src/lib.rs | 3 ++- polkadot/runtime/rococo/src/lib.rs | 2 +- polkadot/runtime/westend/src/lib.rs | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs index 62927807afb8..f19eb73c367e 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs @@ -605,13 +605,14 @@ parameter_types! { parameter_types! { pub const MaxScheduledPerBlock: u32 = 50; pub const MaxScheduledBlocks: u32 = 50; + pub const MaxStaleTaskAge: u32 = 10; } #[cfg(feature = "runtime-benchmarks")] parameter_types! { pub const MaxScheduledPerBlock: u32 = 200; pub const MaxScheduledBlocks: u32 = 200; - pub const MaxStaleTaskAge: u64 = 10; + pub const MaxStaleTaskAge: u32 = 40; } impl pallet_scheduler::Config for Runtime { diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 840408979c5f..0cbe0ab8bf16 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -232,7 +232,7 @@ parameter_types! { BlockWeights::get().max_block; pub const MaxScheduledPerBlock: u32 = 50; pub const MaxScheduledBlocks: u32 = 50; - pub const MaxStaleTaskAge: u64 = 10; + pub const MaxStaleTaskAge: u32 = 10; pub const NoPreimagePostponement: Option = Some(10); } diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index c05ce1228d41..74b58fa71d32 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -233,7 +233,7 @@ parameter_types! { BlockWeights::get().max_block; pub const MaxScheduledPerBlock: u32 = 50; pub const MaxScheduledBlocks: u32 = 50; - pub const MaxStaleTaskAge: u64 = 10; + pub const MaxStaleTaskAge: u32 = 10; pub const NoPreimagePostponement: Option = Some(10); } From ece20279c65b1336abf71afb68fb310ef4fd54db Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Fri, 15 Nov 2024 16:31:38 +0530 Subject: [PATCH 14/16] Removes unused import --- substrate/frame/scheduler/src/benchmarking.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/substrate/frame/scheduler/src/benchmarking.rs b/substrate/frame/scheduler/src/benchmarking.rs index 8cb622f8e59d..e4578fb45a60 100644 --- a/substrate/frame/scheduler/src/benchmarking.rs +++ b/substrate/frame/scheduler/src/benchmarking.rs @@ -27,7 +27,6 @@ use frame_support::{ weights::WeightMeter, }; use frame_system::RawOrigin; -use sp_runtime::bounded_vec; use crate::Pallet as Scheduler; use frame_system::{Call as SystemCall, EventRecord}; From da116a70b2250f8113b40d7c724147fec24586e5 Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Fri, 15 Nov 2024 16:45:19 +0530 Subject: [PATCH 15/16] Minor --- substrate/bin/node/runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index d49d91563762..311e6fa4deef 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -504,7 +504,7 @@ impl pallet_scheduler::Config for Runtime { type MaxScheduledBlocks = ConstU32<512>; #[cfg(not(feature = "runtime-benchmarks"))] type MaxScheduledBlocks = ConstU32<50>; - type MaxStaleTaskAge = ConstU64<10>; + type MaxStaleTaskAge = ConstU32<10>; } impl pallet_glutton::Config for Runtime { From b1b414a4d5d646059d0495585020d3711adee291 Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Fri, 15 Nov 2024 17:16:01 +0530 Subject: [PATCH 16/16] Minor --- substrate/frame/scheduler/src/migration.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/frame/scheduler/src/migration.rs b/substrate/frame/scheduler/src/migration.rs index ae1d351874ac..870ec17e764e 100644 --- a/substrate/frame/scheduler/src/migration.rs +++ b/substrate/frame/scheduler/src/migration.rs @@ -613,7 +613,7 @@ pub mod v5 { } #[cfg(test)] -pub mod test { +pub mod test_v5 { use super::*; use crate::{migration::v5::IncompleteSince, mock::*}; use alloc::vec;