Skip to content

Commit 945377f

Browse files
bkchrshawntabrizi
andauthored
pallet-scheduler: Introduce OriginPrivilegeCmp (paritytech#10078)
* pallet-scheduler: Introduce `OriginPrivilegeCmp` When a scheduled task should be canceled, the origin that tries to cancel the task is compared to the origin the task should be executed with. Before this pr this check only allowed that both origins are equal. However, this is problematic as this means that for example a council origin it needs to be have the same amount of yes votes to cancel the scheduled task. While a council origin with more yes votes should be able to cancel this task. This happened recently on Kusama and lead to a failed cancelation of a scheduled task. With this pr the two origins are compared and the cancelling origin needs to have greater or equal privileges as the origin that scheduled the task. What a greater, equal or less privilege is, can be configured in the runtime. For simplicity, a `EqualPrivilegeOnly` implementation is provided that only checks if two origins are equal. So, this mimics the old behaviour. * FMT * fix import * Small optimizations Co-authored-by: Shawn Tabrizi <[email protected]>
1 parent 69fa67d commit 945377f

File tree

5 files changed

+51
-13
lines changed

5 files changed

+51
-13
lines changed

bin/node/runtime/src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ use codec::{Decode, Encode, MaxEncodedLen};
2626
use frame_support::{
2727
construct_runtime, parameter_types,
2828
traits::{
29-
Currency, Everything, Imbalance, InstanceFilter, KeyOwnerProofSystem, LockIdentifier,
30-
Nothing, OnUnbalanced, U128CurrencyToVote,
29+
Currency, EqualPrivilegeOnly, Everything, Imbalance, InstanceFilter, KeyOwnerProofSystem,
30+
LockIdentifier, Nothing, OnUnbalanced, U128CurrencyToVote,
3131
},
3232
weights::{
3333
constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},
@@ -345,6 +345,7 @@ impl pallet_scheduler::Config for Runtime {
345345
type ScheduleOrigin = EnsureRoot<AccountId>;
346346
type MaxScheduledPerBlock = MaxScheduledPerBlock;
347347
type WeightInfo = pallet_scheduler::weights::SubstrateWeight<Runtime>;
348+
type OriginPrivilegeCmp = EqualPrivilegeOnly;
348349
}
349350

350351
parameter_types! {

frame/democracy/src/tests.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use crate as pallet_democracy;
2222
use codec::Encode;
2323
use frame_support::{
2424
assert_noop, assert_ok, ord_parameter_types, parameter_types,
25-
traits::{Contains, GenesisBuild, OnInitialize, SortedMembers},
25+
traits::{Contains, EqualPrivilegeOnly, GenesisBuild, OnInitialize, SortedMembers},
2626
weights::Weight,
2727
};
2828
use frame_system::{EnsureRoot, EnsureSignedBy};
@@ -118,6 +118,7 @@ impl pallet_scheduler::Config for Test {
118118
type ScheduleOrigin = EnsureRoot<u64>;
119119
type MaxScheduledPerBlock = ();
120120
type WeightInfo = ();
121+
type OriginPrivilegeCmp = EqualPrivilegeOnly;
121122
}
122123
parameter_types! {
123124
pub const ExistentialDeposit: u64 = 1;

frame/scheduler/src/lib.rs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ use frame_support::{
5858
dispatch::{DispatchError, DispatchResult, Dispatchable, Parameter},
5959
traits::{
6060
schedule::{self, DispatchTime},
61-
EnsureOrigin, Get, IsType, OriginTrait,
61+
EnsureOrigin, Get, IsType, OriginTrait, PrivilegeCmp,
6262
},
6363
weights::{GetDispatchInfo, Weight},
6464
};
@@ -69,7 +69,7 @@ use sp_runtime::{
6969
traits::{BadOrigin, One, Saturating, Zero},
7070
RuntimeDebug,
7171
};
72-
use sp_std::{borrow::Borrow, marker::PhantomData, prelude::*};
72+
use sp_std::{borrow::Borrow, cmp::Ordering, marker::PhantomData, prelude::*};
7373
pub use weights::WeightInfo;
7474

7575
/// Just a simple index for naming period tasks.
@@ -160,6 +160,15 @@ pub mod pallet {
160160
/// Required origin to schedule or cancel calls.
161161
type ScheduleOrigin: EnsureOrigin<<Self as system::Config>::Origin>;
162162

163+
/// Compare the privileges of origins.
164+
///
165+
/// This will be used when canceling a task, to ensure that the origin that tries
166+
/// to cancel has greater or equal privileges as the origin that created the scheduled task.
167+
///
168+
/// For simplicity the [`EqualPrivilegeOnly`](frame_support::traits::EqualPrivilegeOnly) can
169+
/// be used. This will only check if two given origins are equal.
170+
type OriginPrivilegeCmp: PrivilegeCmp<Self::PalletsOrigin>;
171+
163172
/// The maximum number of scheduled calls in the queue for a single block.
164173
/// Not strictly enforced, but used for weight estimation.
165174
#[pallet::constant]
@@ -614,7 +623,10 @@ impl<T: Config> Pallet<T> {
614623
Ok(None),
615624
|s| -> Result<Option<Scheduled<_, _, _, _>>, DispatchError> {
616625
if let (Some(ref o), Some(ref s)) = (origin, s.borrow()) {
617-
if *o != s.origin {
626+
if matches!(
627+
T::OriginPrivilegeCmp::cmp_privilege(o, &s.origin),
628+
Some(Ordering::Less) | None
629+
) {
618630
return Err(BadOrigin.into())
619631
}
620632
};
@@ -709,7 +721,10 @@ impl<T: Config> Pallet<T> {
709721
Agenda::<T>::try_mutate(when, |agenda| -> DispatchResult {
710722
if let Some(s) = agenda.get_mut(i) {
711723
if let (Some(ref o), Some(ref s)) = (origin, s.borrow()) {
712-
if *o != s.origin {
724+
if matches!(
725+
T::OriginPrivilegeCmp::cmp_privilege(o, &s.origin),
726+
Some(Ordering::Less) | None
727+
) {
713728
return Err(BadOrigin.into())
714729
}
715730
}
@@ -832,7 +847,7 @@ mod tests {
832847
use crate as scheduler;
833848
use frame_support::{
834849
assert_err, assert_noop, assert_ok, ord_parameter_types, parameter_types,
835-
traits::{Contains, OnFinalize, OnInitialize},
850+
traits::{Contains, EqualPrivilegeOnly, OnFinalize, OnInitialize},
836851
weights::constants::RocksDbWeight,
837852
Hashable,
838853
};
@@ -980,6 +995,7 @@ mod tests {
980995
type ScheduleOrigin = EnsureOneOf<u64, EnsureRoot<u64>, EnsureSignedBy<One, u64>>;
981996
type MaxScheduledPerBlock = MaxScheduledPerBlock;
982997
type WeightInfo = ();
998+
type OriginPrivilegeCmp = EqualPrivilegeOnly;
983999
}
9841000

9851001
pub type LoggerCall = logger::Call<Test>;

frame/support/src/traits.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,10 @@ pub use filter::{ClearFilterGuard, FilterStack, FilterStackGuard, InstanceFilter
5050

5151
mod misc;
5252
pub use misc::{
53-
Backing, ConstU32, EnsureInherentsAreFirst, EstimateCallFee, ExecuteBlock, ExtrinsicCall, Get,
54-
GetBacking, GetDefault, HandleLifetime, IsSubType, IsType, Len, OffchainWorker,
55-
OnKilledAccount, OnNewAccount, SameOrOther, Time, TryDrop, UnixTime, WrapperKeepOpaque,
56-
WrapperOpaque,
53+
Backing, ConstU32, EnsureInherentsAreFirst, EqualPrivilegeOnly, EstimateCallFee, ExecuteBlock,
54+
ExtrinsicCall, Get, GetBacking, GetDefault, HandleLifetime, IsSubType, IsType, Len,
55+
OffchainWorker, OnKilledAccount, OnNewAccount, PrivilegeCmp, SameOrOther, Time, TryDrop,
56+
UnixTime, WrapperKeepOpaque, WrapperOpaque,
5757
};
5858

5959
mod stored_map;

frame/support/src/traits/misc.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use crate::dispatch::Parameter;
2121
use codec::{CompactLen, Decode, DecodeAll, Encode, EncodeLike, Input, MaxEncodedLen};
2222
use scale_info::{build::Fields, meta_type, Path, Type, TypeInfo, TypeParameter};
2323
use sp_runtime::{traits::Block as BlockT, DispatchError};
24-
use sp_std::prelude::*;
24+
use sp_std::{cmp::Ordering, prelude::*};
2525

2626
/// Anything that can have a `::len()` method.
2727
pub trait Len {
@@ -289,6 +289,26 @@ pub trait ExecuteBlock<Block: BlockT> {
289289
fn execute_block(block: Block);
290290
}
291291

292+
/// Something that can compare privileges of two origins.
293+
pub trait PrivilegeCmp<Origin> {
294+
/// Compare the `left` to the `right` origin.
295+
///
296+
/// The returned ordering should be from the pov of the `left` origin.
297+
///
298+
/// Should return `None` when it can not compare the given origins.
299+
fn cmp_privilege(left: &Origin, right: &Origin) -> Option<Ordering>;
300+
}
301+
302+
/// Implementation of [`PrivilegeCmp`] that only checks for equal origins.
303+
///
304+
/// This means it will either return [`Origin::Equal`] or `None`.
305+
pub struct EqualPrivilegeOnly;
306+
impl<Origin: PartialEq> PrivilegeCmp<Origin> for EqualPrivilegeOnly {
307+
fn cmp_privilege(left: &Origin, right: &Origin) -> Option<Ordering> {
308+
(left == right).then(|| Ordering::Equal)
309+
}
310+
}
311+
292312
/// Off-chain computation trait.
293313
///
294314
/// Implementing this trait on a module allows you to perform long-running tasks

0 commit comments

Comments
 (0)