Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Scheduler to Support Relay Chain Block Number Provider #6362

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -622,6 +624,8 @@ impl pallet_scheduler::Config for Runtime {
type WeightInfo = weights::pallet_scheduler::WeightInfo<Runtime>;
type OriginPrivilegeCmp = EqualOrGreatestRootCmp;
type Preimages = Preimage;
type BlockNumberProvider = frame_system::Pallet<Runtime>;
type MaxScheduledBlocks = MaxScheduledBlocks;
}

parameter_types! {
Expand Down
3 changes: 3 additions & 0 deletions polkadot/runtime/rococo/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u32> = Some(10);
}

Expand Down Expand Up @@ -331,6 +332,8 @@ impl pallet_scheduler::Config for Runtime {
type WeightInfo = weights::pallet_scheduler::WeightInfo<Runtime>;
type OriginPrivilegeCmp = OriginPrivilegeCmp;
type Preimages = Preimage;
type BlockNumberProvider = frame_system::Pallet<Runtime>;
type MaxScheduledBlocks = MaxScheduledBlocks;
}

parameter_types! {
Expand Down
3 changes: 3 additions & 0 deletions polkadot/runtime/westend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,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<u32> = Some(10);
}

Expand All @@ -248,6 +249,8 @@ impl pallet_scheduler::Config for Runtime {
type WeightInfo = weights::pallet_scheduler::WeightInfo<Runtime>;
type OriginPrivilegeCmp = frame_support::traits::EqualPrivilegeOnly;
type Preimages = Preimage;
type BlockNumberProvider = frame_system::Pallet<Runtime>;
type MaxScheduledBlocks = MaxScheduledBlocks;
}

parameter_types! {
Expand Down
23 changes: 23 additions & 0 deletions prdoc/pr_6362.prdoc
Original file line number Diff line number Diff line change
@@ -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 via a `Queue` to handle multiple blocks with valid
agenda passed between them.

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

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
5 changes: 5 additions & 0 deletions substrate/bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,11 @@ impl pallet_scheduler::Config for Runtime {
type WeightInfo = pallet_scheduler::weights::SubstrateWeight<Runtime>;
type OriginPrivilegeCmp = EqualPrivilegeOnly;
type Preimages = Preimage;
type BlockNumberProvider = frame_system::Pallet<Runtime>;
#[cfg(feature = "runtime-benchmarks")]
type MaxScheduledBlocks = ConstU32<512>;
#[cfg(not(feature = "runtime-benchmarks"))]
type MaxScheduledBlocks = ConstU32<50>;
}

impl pallet_glutton::Config for Runtime {
Expand Down
2 changes: 2 additions & 0 deletions substrate/frame/democracy/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ impl pallet_scheduler::Config for Test {
type WeightInfo = ();
type OriginPrivilegeCmp = EqualPrivilegeOnly;
type Preimages = ();
type BlockNumberProvider = frame_system::Pallet<Test>;
type MaxScheduledBlocks = ConstU32<100>;
}

#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)]
Expand Down
2 changes: 2 additions & 0 deletions substrate/frame/referenda/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ impl pallet_scheduler::Config for Test {
type WeightInfo = ();
type OriginPrivilegeCmp = EqualPrivilegeOnly;
type Preimages = Preimage;
type BlockNumberProvider = frame_system::Pallet<Test>;
type MaxScheduledBlocks = ConstU32<100>;
}
#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)]
impl pallet_balances::Config for Test {
Expand Down
77 changes: 54 additions & 23 deletions substrate/frame/scheduler/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T: Config>() -> u32 {
T::MaxScheduledBlocks::get()
}

type SystemOrigin<T> = <T as frame_system::Config>::RuntimeOrigin;

Expand All @@ -44,17 +47,23 @@ fn assert_last_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
assert_eq!(event, &system_event);
}

fn fill_queue<T: Config>(n: u32) {
let mut vec = Vec::<BlockNumberFor<T>>::new();
for i in 0..n {
vec.push(i.into());
}
let bounded_vec = BoundedVec::try_from(vec).unwrap();
Queue::<T>::put::<BoundedVec<_, _>>(bounded_vec);
}

/// 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<T: Config>(
when: frame_system::pallet_prelude::BlockNumberFor<T>,
n: u32,
) -> Result<(), &'static str> {
fn fill_schedule<T: Config>(when: BlockNumberFor<T>, n: u32) -> Result<(), &'static str> {
let t = DispatchTime::At(when);
let origin: <T as Config>::PalletsOrigin = frame_system::RawOrigin::Root.into();
for i in 0..n {
Expand Down Expand Up @@ -137,17 +146,19 @@ fn make_origin<T: Config>(signed: bool) -> <T as Config>::PalletsOrigin {
benchmarks! {
// `service_agendas` when no work is done.
service_agendas_base {
let now = BlockNumberFor::<T>::from(BLOCK_NUMBER);
IncompleteSince::<T>::put(now - One::one());
let now = BlockNumberFor::<T>::from(block_number::<T>());
Queue::<T>::put::<BoundedVec<_, _>>(bounded_vec![now + One::one()]);
}: {
Scheduler::<T>::service_agendas(&mut WeightMeter::new(), now, 0);
} verify {
assert_eq!(IncompleteSince::<T>::get(), Some(now - One::one()));
let expected: BoundedVec<BlockNumberFor<T>, T::MaxScheduledBlocks> =
bounded_vec![now + One::one()];
assert_eq!(Queue::<T>::get(), expected);
}

// `service_agenda` when no work is done.
service_agenda_base {
let now = BLOCK_NUMBER.into();
let now = block_number::<T>().into();
let s in 0 .. T::MaxScheduledPerBlock::get();
fill_schedule::<T>(now, s)?;
let mut executed = 0;
Expand All @@ -160,7 +171,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::<T>().into();
let task = make_task::<T>(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());
Expand All @@ -178,7 +189,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::<T>().into();
let task = make_task::<T>(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());
Expand All @@ -190,7 +201,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::<T>().into();
let task = make_task::<T>(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());
Expand All @@ -202,7 +213,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::<T>().into();
let task = make_task::<T>(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());
Expand Down Expand Up @@ -235,26 +246,32 @@ benchmarks! {

schedule {
let s in 0 .. (T::MaxScheduledPerBlock::get() - 1);
let when = BLOCK_NUMBER.into();
let when = block_number::<T>().into();
let periodic = Some((BlockNumberFor::<T>::one(), 100));
let priority = 0;
// Essentially a no-op call.
let call = Box::new(SystemCall::set_storage { items: vec![] }.into());

fill_schedule::<T>(when, s)?;
fill_queue::<T>(T::MaxScheduledBlocks::get() - 1);
}: _(RawOrigin::Root, when, periodic, priority, call)
verify {
ensure!(
Agenda::<T>::get(when).len() == (s + 1) as usize,
"didn't add to schedule"
);
ensure!(
Queue::<T>::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::<T>() - 1).into();

fill_schedule::<T>(when, s)?;
fill_queue::<T>(T::MaxScheduledBlocks::get());
assert_eq!(Agenda::<T>::get(when).len(), s as usize);
let schedule_origin =
T::ScheduleOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
Expand All @@ -273,31 +290,41 @@ benchmarks! {
s > 1 || Agenda::<T>::get(when).len() == 0,
"remove from schedule if only 1 task scheduled for `when`"
);
ensure!(
s > 1 || Queue::<T>::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::<T>().into();
let periodic = Some((BlockNumberFor::<T>::one(), 100));
let priority = 0;
// Essentially a no-op call.
let call = Box::new(SystemCall::set_storage { items: vec![] }.into());

fill_schedule::<T>(when, s)?;
fill_queue::<T>(T::MaxScheduledBlocks::get() - 1);
}: _(RawOrigin::Root, id, when, periodic, priority, call)
verify {
ensure!(
Agenda::<T>::get(when).len() == (s + 1) as usize,
"didn't add to schedule"
);
ensure!(
Queue::<T>::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::<T>() - 1).into();

fill_schedule::<T>(when, s)?;
fill_queue::<T>(T::MaxScheduledBlocks::get());
}: _(RawOrigin::Root, u32_to_name(0))
verify {
ensure!(
Expand All @@ -313,11 +340,15 @@ benchmarks! {
s > 1 || Agenda::<T>::get(when).len() == 0,
"remove from schedule if only 1 task scheduled for `when`"
);
ensure!(
s > 1 || Queue::<T>::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::<T>().into();

fill_schedule::<T>(when, s)?;
let name = u32_to_name(s - 1);
Expand All @@ -341,7 +372,7 @@ benchmarks! {

set_retry {
let s = T::MaxScheduledPerBlock::get();
let when = BLOCK_NUMBER.into();
let when = block_number::<T>().into();

fill_schedule::<T>(when, s)?;
let name = u32_to_name(s - 1);
Expand All @@ -361,7 +392,7 @@ benchmarks! {

set_retry_named {
let s = T::MaxScheduledPerBlock::get();
let when = BLOCK_NUMBER.into();
let when = block_number::<T>().into();

fill_schedule::<T>(when, s)?;
let name = u32_to_name(s - 1);
Expand All @@ -381,7 +412,7 @@ benchmarks! {

cancel_retry {
let s = T::MaxScheduledPerBlock::get();
let when = BLOCK_NUMBER.into();
let when = block_number::<T>().into();

fill_schedule::<T>(when, s)?;
let name = u32_to_name(s - 1);
Expand All @@ -399,7 +430,7 @@ benchmarks! {

cancel_retry_named {
let s = T::MaxScheduledPerBlock::get();
let when = BLOCK_NUMBER.into();
let when = block_number::<T>().into();

fill_schedule::<T>(when, s)?;
let name = u32_to_name(s - 1);
Expand Down
Loading
Loading