diff --git a/Cargo.lock b/Cargo.lock index 89b24d0826f97..fb8ab90ccc530 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4423,6 +4423,7 @@ dependencies = [ "sp-io", "sp-runtime", "sp-std", + "substrate-test-utils", ] [[package]] diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index c5c11fe577a3f..7bec203f8c446 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -97,8 +97,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // and set impl_version to 0. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. - spec_version: 254, - impl_version: 1, + spec_version: 255, + impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, }; @@ -255,8 +255,10 @@ parameter_types! { impl pallet_scheduler::Trait for Runtime { type Event = Event; type Origin = Origin; + type PalletsOrigin = OriginCaller; type Call = Call; type MaximumWeight = MaximumSchedulerWeight; + type ScheduleOrigin = EnsureRoot; } parameter_types! { @@ -455,6 +457,7 @@ impl pallet_democracy::Trait for Runtime { type OperationalPreimageOrigin = pallet_collective::EnsureMember; type Slash = Treasury; type Scheduler = Scheduler; + type PalletsOrigin = OriginCaller; type MaxVotes = MaxVotes; } diff --git a/frame/collective/src/lib.rs b/frame/collective/src/lib.rs index 2be0241243524..83116080d0943 100644 --- a/frame/collective/src/lib.rs +++ b/frame/collective/src/lib.rs @@ -97,7 +97,7 @@ pub trait Trait: frame_system::Trait { } /// Origin for the collective module. -#[derive(PartialEq, Eq, Clone, RuntimeDebug)] +#[derive(PartialEq, Eq, Clone, RuntimeDebug, Encode, Decode)] pub enum RawOrigin { /// It has been condoned by a given number of members of the collective from a given total. Members(MemberCount, MemberCount), diff --git a/frame/democracy/src/benchmarking.rs b/frame/democracy/src/benchmarking.rs index d0bd73244825e..c8025b2999d81 100644 --- a/frame/democracy/src/benchmarking.rs +++ b/frame/democracy/src/benchmarking.rs @@ -79,6 +79,7 @@ fn add_referendum(n: u32) -> Result { 0.into(), None, 63, + system::RawOrigin::Root.into(), Call::enact_proposal(proposal_hash, referendum_index).into(), ); Ok(referendum_index) diff --git a/frame/democracy/src/lib.rs b/frame/democracy/src/lib.rs index 79cc136d45875..b005ad3641a0b 100644 --- a/frame/democracy/src/lib.rs +++ b/frame/democracy/src/lib.rs @@ -279,7 +279,10 @@ pub trait Trait: frame_system::Trait + Sized { type Slash: OnUnbalanced>; /// The Scheduler. - type Scheduler: ScheduleNamed; + type Scheduler: ScheduleNamed; + + /// Overarching type of all pallets origins. + type PalletsOrigin: From>; /// The maximum number of votes for an account. /// @@ -1625,6 +1628,7 @@ impl Module { when, None, 63, + system::RawOrigin::Root.into(), Call::enact_proposal(status.proposal_hash, index).into(), ).is_err() { frame_support::print("LOGIC ERROR: bake_referendum/schedule_named failed"); diff --git a/frame/democracy/src/tests.rs b/frame/democracy/src/tests.rs index b92f4bd0760f6..2f300ec8bc61d 100644 --- a/frame/democracy/src/tests.rs +++ b/frame/democracy/src/tests.rs @@ -31,7 +31,7 @@ use sp_runtime::{ testing::Header, Perbill, }; use pallet_balances::{BalanceLock, Error as BalancesError}; -use frame_system::EnsureSignedBy; +use frame_system::{EnsureSignedBy, EnsureRoot}; mod cancellation; mod delegation; @@ -123,8 +123,10 @@ parameter_types! { impl pallet_scheduler::Trait for Test { type Event = Event; type Origin = Origin; + type PalletsOrigin = OriginCaller; type Call = Call; type MaximumWeight = MaximumSchedulerWeight; + type ScheduleOrigin = EnsureRoot; } parameter_types! { pub const ExistentialDeposit: u64 = 1; @@ -196,6 +198,7 @@ impl super::Trait for Test { type Scheduler = Scheduler; type MaxVotes = MaxVotes; type OperationalPreimageOrigin = EnsureSignedBy; + type PalletsOrigin = OriginCaller; } pub fn new_test_ext() -> sp_io::TestExternalities { diff --git a/frame/scheduler/Cargo.toml b/frame/scheduler/Cargo.toml index 43507bd364f0f..003a8c20c504c 100644 --- a/frame/scheduler/Cargo.toml +++ b/frame/scheduler/Cargo.toml @@ -21,6 +21,7 @@ frame-benchmarking = { version = "2.0.0-rc4", default-features = false, path = " [dev-dependencies] sp-core = { version = "2.0.0-rc4", path = "../../primitives/core", default-features = false } +substrate-test-utils = { version = "2.0.0-rc4", path = "../../test-utils" } [features] default = ["std"] @@ -34,4 +35,8 @@ std = [ "sp-io/std", "sp-std/std" ] -runtime-benchmarks = ["frame-benchmarking"] +runtime-benchmarks = [ + "frame-benchmarking", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", +] diff --git a/frame/scheduler/src/benchmarking.rs b/frame/scheduler/src/benchmarking.rs index 975c10e3b6c8f..099b18f7f3149 100644 --- a/frame/scheduler/src/benchmarking.rs +++ b/frame/scheduler/src/benchmarking.rs @@ -43,6 +43,7 @@ fn fill_schedule (when: T::BlockNumber, n: u32) -> Result<(), &'static Some((T::BlockNumber::one(), 100)), // HARD_DEADLINE priority means it gets executed no matter what 0, + frame_system::RawOrigin::Root.into(), call.clone().into(), )?; } diff --git a/frame/scheduler/src/lib.rs b/frame/scheduler/src/lib.rs index 6b47e6258708e..1b3517382f789 100644 --- a/frame/scheduler/src/lib.rs +++ b/frame/scheduler/src/lib.rs @@ -28,6 +28,14 @@ //! specified block number or at a specified period. These scheduled dispatches //! may be named or anonymous and may be canceled. //! +//! **NOTE:** The scheduled calls will be dispatched with the default filter +//! for the origin: namely `frame_system::Trait::BaseCallFilter` for all origin +//! except root which will get no filter. And not the filter contained in origin +//! use to call `fn schedule`. +//! +//! If a call is scheduled using proxy or whatever mecanism which adds filter, +//! then those filter will not be used when dispatching the schedule call. +//! //! ## Interface //! //! ### Dispatchable Functions @@ -45,16 +53,16 @@ mod benchmarking; -use sp_std::prelude::*; -use codec::{Encode, Decode}; -use sp_runtime::{RuntimeDebug, traits::{Zero, One}}; +use sp_std::{prelude::*, marker::PhantomData, borrow::Borrow}; +use codec::{Encode, Decode, Codec}; +use sp_runtime::{RuntimeDebug, traits::{Zero, One, BadOrigin}}; use frame_support::{ - decl_module, decl_storage, decl_event, decl_error, + decl_module, decl_storage, decl_event, decl_error, IterableStorageMap, dispatch::{Dispatchable, DispatchError, DispatchResult, Parameter}, - traits::{Get, schedule}, + traits::{Get, schedule, OriginTrait, EnsureOrigin, IsType}, weights::{GetDispatchInfo, Weight}, }; -use frame_system::{self as system, ensure_root}; +use frame_system::{self as system}; /// Our pallet's configuration trait. All our types and constants go in here. If the /// pallet is dependent on specific other pallets, then their configuration traits @@ -66,7 +74,11 @@ pub trait Trait: system::Trait { type Event: From> + Into<::Event>; /// The aggregated origin which the dispatch will take. - type Origin: From>; + type Origin: OriginTrait + From + IsType<::Origin>; + + /// The caller origin, overarching type of all pallets origins. + type PalletsOrigin: From> + Codec + Clone + Eq; /// The aggregated call type. type Call: Parameter + Dispatchable::Origin> + GetDispatchInfo + From>; @@ -74,6 +86,9 @@ pub trait Trait: system::Trait { /// The maximum weight that may be scheduled per block for any dispatchables of less priority /// than `schedule::HARD_DEADLINE`. type MaximumWeight: Get; + + /// Required origin to schedule or cancel calls. + type ScheduleOrigin: EnsureOrigin<::Origin>; } /// Just a simple index for naming period tasks. @@ -81,9 +96,19 @@ pub type PeriodicIndex = u32; /// The location of a scheduled task that can be used to remove it. pub type TaskAddress = (BlockNumber, u32); +#[cfg_attr(any(feature = "std", test), derive(PartialEq, Eq))] +#[derive(Clone, RuntimeDebug, Encode, Decode)] +struct ScheduledV1 { + maybe_id: Option>, + priority: schedule::Priority, + call: Call, + maybe_periodic: Option>, +} + /// Information regarding an item to be executed in the future. +#[cfg_attr(any(feature = "std", test), derive(PartialEq, Eq))] #[derive(Clone, RuntimeDebug, Encode, Decode)] -pub struct Scheduled { +pub struct ScheduledV2 { /// The unique identity for this task, if there is one. maybe_id: Option>, /// This task's priority. @@ -92,16 +117,42 @@ pub struct Scheduled { call: Call, /// If the call is periodic, then this points to the information concerning that. maybe_periodic: Option>, + /// The origin to dispatch the call. + origin: PalletsOrigin, + _phantom: PhantomData, +} + +/// The current version of Scheduled struct. +pub type Scheduled = ScheduledV2; + +// A value placed in storage that represents the current version of the Scheduler storage. +// This value is used by the `on_runtime_upgrade` logic to determine whether we run +// storage migration logic. +#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug)] +enum Releases { + V1, + V2, +} + +impl Default for Releases { + fn default() -> Self { + Releases::V1 + } } decl_storage! { trait Store for Module as Scheduler { /// Items to be executed, indexed by the block number that they should be executed on. pub Agenda: map hasher(twox_64_concat) T::BlockNumber - => Vec::Call, T::BlockNumber>>>; + => Vec::Call, T::BlockNumber, T::PalletsOrigin, T::AccountId>>>; /// Lookup from identity to the block number and index of the task. Lookup: map hasher(twox_64_concat) Vec => Option>; + + /// Storage version of the pallet. + /// + /// New networks start with last version. + StorageVersion build(|_| Releases::V2): Releases; } } @@ -127,6 +178,7 @@ decl_error! { decl_module! { /// Scheduler module declaration. pub struct Module for enum Call where origin: ::Origin { + type Error = Error; fn deposit_event() = default; /// Anonymously schedule a task. @@ -146,8 +198,9 @@ decl_module! { priority: schedule::Priority, call: Box<::Call>, ) { - ensure_root(origin)?; - Self::do_schedule(when, maybe_periodic, priority, *call)?; + T::ScheduleOrigin::ensure_origin(origin.clone())?; + let origin = ::Origin::from(origin); + Self::do_schedule(when, maybe_periodic, priority, origin.caller().clone(), *call)?; } /// Cancel an anonymously scheduled task. @@ -162,8 +215,9 @@ decl_module! { /// # #[weight = 100_000_000 + T::DbWeight::get().reads_writes(1, 2)] fn cancel(origin, when: T::BlockNumber, index: u32) { - ensure_root(origin)?; - Self::do_cancel((when, index))?; + T::ScheduleOrigin::ensure_origin(origin.clone())?; + let origin = ::Origin::from(origin); + Self::do_cancel(Some(origin.caller().clone()), (when, index))?; } /// Schedule a named task. @@ -184,8 +238,9 @@ decl_module! { priority: schedule::Priority, call: Box<::Call>, ) { - ensure_root(origin)?; - Self::do_schedule_named(id, when, maybe_periodic, priority, *call)?; + T::ScheduleOrigin::ensure_origin(origin.clone())?; + let origin = ::Origin::from(origin); + Self::do_schedule_named(id, when, maybe_periodic, priority, origin.caller().clone(), *call)?; } /// Cancel a named scheduled task. @@ -200,8 +255,9 @@ decl_module! { /// # #[weight = 100_000_000 + T::DbWeight::get().reads_writes(2, 2)] fn cancel_named(origin, id: Vec) { - ensure_root(origin)?; - Self::do_cancel_named(id)?; + T::ScheduleOrigin::ensure_origin(origin.clone())?; + let origin = ::Origin::from(origin); + Self::do_cancel_named(Some(origin.caller().clone()), id)?; } /// Execute the scheduled calls @@ -249,7 +305,7 @@ decl_module! { // - It does not push the weight past the limit. // - It is the first item in the schedule if s.priority <= schedule::HARD_DEADLINE || cumulative_weight <= limit || order == 0 { - let r = s.call.clone().dispatch(system::RawOrigin::Root.into()); + let r = s.call.clone().dispatch(s.origin.clone().into()); let maybe_id = s.maybe_id.clone(); if let &Some((period, count)) = &s.maybe_periodic { if count > 1 { @@ -291,10 +347,39 @@ decl_module! { } impl Module { + /// Migrate storage format from V1 to V2. + /// Return true if migration is performed. + pub fn migrate_v1_to_t2() -> bool { + if StorageVersion::get() == Releases::V1 { + StorageVersion::put(Releases::V2); + + Agenda::::translate::< + Vec::Call, T::BlockNumber>>>, _ + >(|_, agenda| Some( + agenda + .into_iter() + .map(|schedule| schedule.map(|schedule| ScheduledV2 { + maybe_id: schedule.maybe_id, + priority: schedule.priority, + call: schedule.call, + maybe_periodic: schedule.maybe_periodic, + origin: system::RawOrigin::Root.into(), + _phantom: Default::default(), + })) + .collect::>() + )); + + true + } else { + false + } + } + fn do_schedule( when: T::BlockNumber, maybe_periodic: Option>, priority: schedule::Priority, + origin: T::PalletsOrigin, call: ::Call ) -> Result, DispatchError> { if when <= frame_system::Module::::block_number() { @@ -306,7 +391,9 @@ impl Module { .filter(|p| p.1 > 1 && !p.0.is_zero()) // Remove one from the number of repetitions since we will schedule one now. .map(|(p, c)| (p, c - 1)); - let s = Some(Scheduled { maybe_id: None, priority, call, maybe_periodic }); + let s = Some(Scheduled { + maybe_id: None, priority, call, maybe_periodic, origin, _phantom: PhantomData::::default(), + }); Agenda::::append(when, s); let index = Agenda::::decode_len(when).unwrap_or(1) as u32 - 1; Self::deposit_event(RawEvent::Scheduled(when, index)); @@ -314,8 +401,25 @@ impl Module { Ok((when, index)) } - fn do_cancel((when, index): TaskAddress) -> Result<(), DispatchError> { - if let Some(s) = Agenda::::mutate(when, |agenda| agenda.get_mut(index as usize).and_then(Option::take)) { + 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 *o != s.origin { + return Err(BadOrigin.into()); + } + }; + Ok(s.take()) + }) + }, + )?; + if let Some(s) = scheduled { if let Some(id) = s.maybe_id { Lookup::::remove(id); } @@ -331,6 +435,7 @@ impl Module { when: T::BlockNumber, maybe_periodic: Option>, priority: schedule::Priority, + origin: T::PalletsOrigin, call: ::Call, ) -> Result, DispatchError> { // ensure id it is unique @@ -348,7 +453,9 @@ impl Module { // Remove one from the number of repetitions since we will schedule one now. .map(|(p, c)| (p, c - 1)); - let s = Scheduled { maybe_id: Some(id.clone()), priority, call, maybe_periodic }; + let s = Scheduled { + maybe_id: Some(id.clone()), priority, call, maybe_periodic, origin, _phantom: Default::default() + }; Agenda::::append(when, Some(s)); let index = Agenda::::decode_len(when).unwrap_or(1) as u32 - 1; let address = (when, index); @@ -358,36 +465,49 @@ impl Module { Ok(address) } - fn do_cancel_named(id: Vec) -> Result<(), DispatchError> { - if let Some((when, index)) = Lookup::::take(id) { - let i = index as usize; - Agenda::::mutate(when, |agenda| if let Some(s) = agenda.get_mut(i) { *s = None }); - Self::deposit_event(RawEvent::Canceled(when, index)); - Ok(()) - } else { - Err(Error::::FailedToCancel)? - } + fn do_cancel_named(origin: Option, id: Vec) -> DispatchResult { + Lookup::::try_mutate_exists(id, |lookup| -> DispatchResult { + if let Some((when, index)) = lookup.take() { + let i = index as usize; + Agenda::::try_mutate(when, |agenda| -> DispatchResult { + if let Some(s) = agenda.get_mut(i) { + if let (Some(ref o), Some(ref s)) = (origin, s.borrow()) { + if *o != s.origin { + return Err(BadOrigin.into()); + } + } + *s = None; + } + Ok(()) + })?; + Self::deposit_event(RawEvent::Canceled(when, index)); + Ok(()) + } else { + Err(Error::::FailedToCancel)? + } + }) } } -impl schedule::Anon::Call> for Module { +impl schedule::Anon::Call, T::PalletsOrigin> for Module { type Address = TaskAddress; fn schedule( when: T::BlockNumber, maybe_periodic: Option>, priority: schedule::Priority, + origin: T::PalletsOrigin, call: ::Call ) -> Result { - Self::do_schedule(when, maybe_periodic, priority, call) + Self::do_schedule(when, maybe_periodic, priority, origin, call) } fn cancel((when, index): Self::Address) -> Result<(), ()> { - Self::do_cancel((when, index)).map_err(|_| ()) + Self::do_cancel(None, (when, index)).map_err(|_| ()) } } -impl schedule::Named::Call> for Module { +impl schedule::Named::Call, T::PalletsOrigin> for Module { type Address = TaskAddress; fn schedule_named( @@ -395,13 +515,14 @@ impl schedule::Named::Call> for Module when: T::BlockNumber, maybe_periodic: Option>, priority: schedule::Priority, + origin: T::PalletsOrigin, call: ::Call, ) -> Result { - Self::do_schedule_named(id, when, maybe_periodic, priority, call).map_err(|_| ()) + Self::do_schedule_named(id, when, maybe_periodic, priority, origin, call).map_err(|_| ()) } fn cancel_named(id: Vec) -> Result<(), ()> { - Self::do_cancel_named(id).map_err(|_| ()) + Self::do_cancel_named(None, id).map_err(|_| ()) } } @@ -410,8 +531,10 @@ mod tests { use super::*; use frame_support::{ - impl_outer_event, impl_outer_origin, impl_outer_dispatch, parameter_types, assert_ok, - assert_err, traits::{OnInitialize, OnFinalize, Filter}, weights::constants::RocksDbWeight, + impl_outer_event, impl_outer_origin, impl_outer_dispatch, parameter_types, assert_ok, ord_parameter_types, + assert_noop, assert_err, Hashable, + traits::{OnInitialize, OnFinalize, Filter}, + weights::constants::RocksDbWeight, }; use sp_core::H256; // The testing primitives are very useful for avoiding having to work with signatures @@ -421,41 +544,48 @@ mod tests { testing::Header, traits::{BlakeTwo256, IdentityLookup}, }; + use frame_system::{EnsureOneOf, EnsureRoot, EnsureSignedBy}; use crate as scheduler; mod logger { use super::*; use std::cell::RefCell; - use frame_system::ensure_root; thread_local! { - static LOG: RefCell> = RefCell::new(Vec::new()); + static LOG: RefCell> = RefCell::new(Vec::new()); } - pub fn log() -> Vec { + pub fn log() -> Vec<(OriginCaller, u32)> { LOG.with(|log| log.borrow().clone()) } pub trait Trait: system::Trait { type Event: From + Into<::Event>; } - decl_storage! { - trait Store for Module as Logger { - } - } decl_event! { pub enum Event { Logged(u32, Weight), } } decl_module! { - pub struct Module for enum Call where origin: ::Origin { + pub struct Module for enum Call + where + origin: ::Origin, + ::Origin: OriginTrait + { fn deposit_event() = default; #[weight = *weight] fn log(origin, i: u32, weight: Weight) { - ensure_root(origin)?; Self::deposit_event(Event::Logged(i, weight)); LOG.with(|log| { - log.borrow_mut().push(i); + log.borrow_mut().push((origin.caller().clone(), i)); + }) + } + + #[weight = *weight] + fn log_without_filter(origin, i: u32, weight: Weight) { + Self::deposit_event(Event::Logged(i, weight)); + LOG.with(|log| { + log.borrow_mut().push((origin.caller().clone(), i)); }) } } @@ -485,7 +615,7 @@ mod tests { pub struct BaseFilter; impl Filter for BaseFilter { fn filter(call: &Call) -> bool { - !matches!(call, Call::Logger(_)) + !matches!(call, Call::Logger(logger::Call::log(_, _))) } } @@ -532,11 +662,17 @@ mod tests { parameter_types! { pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * MaximumBlockWeight::get(); } + ord_parameter_types! { + pub const One: u64 = 1; + } + impl Trait for Test { type Event = (); type Origin = Origin; + type PalletsOrigin = OriginCaller; type Call = Call; type MaximumWeight = MaximumSchedulerWeight; + type ScheduleOrigin = EnsureOneOf, EnsureSignedBy>; } type System = system::Module; type Logger = logger::Module; @@ -557,18 +693,22 @@ mod tests { } } + fn root() -> OriginCaller { + system::RawOrigin::Root.into() + } + #[test] fn basic_scheduling_works() { new_test_ext().execute_with(|| { let call = Call::Logger(logger::Call::log(42, 1000)); assert!(!::BaseCallFilter::filter(&call)); - let _ = Scheduler::do_schedule(4, None, 127, call); + let _ = Scheduler::do_schedule(4, None, 127, root(), call); run_to_block(3); assert!(logger::log().is_empty()); run_to_block(4); - assert_eq!(logger::log(), vec![42u32]); + assert_eq!(logger::log(), vec![(root(), 42u32)]); run_to_block(100); - assert_eq!(logger::log(), vec![42u32]); + assert_eq!(logger::log(), vec![(root(), 42u32)]); }); } @@ -576,21 +716,23 @@ mod tests { fn periodic_scheduling_works() { new_test_ext().execute_with(|| { // at #4, every 3 blocks, 3 times. - let _ = Scheduler::do_schedule(4, Some((3, 3)), 127, Call::Logger(logger::Call::log(42, 1000))); + let _ = Scheduler::do_schedule( + 4, Some((3, 3)), 127, root(), Call::Logger(logger::Call::log(42, 1000)) + ); run_to_block(3); assert!(logger::log().is_empty()); run_to_block(4); - assert_eq!(logger::log(), vec![42u32]); + assert_eq!(logger::log(), vec![(root(), 42u32)]); run_to_block(6); - assert_eq!(logger::log(), vec![42u32]); + assert_eq!(logger::log(), vec![(root(), 42u32)]); run_to_block(7); - assert_eq!(logger::log(), vec![42u32, 42u32]); + assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]); run_to_block(9); - assert_eq!(logger::log(), vec![42u32, 42u32]); + assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]); run_to_block(10); - assert_eq!(logger::log(), vec![42u32, 42u32, 42u32]); + assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32), (root(), 42u32)]); run_to_block(100); - assert_eq!(logger::log(), vec![42u32, 42u32, 42u32]); + assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32), (root(), 42u32)]); }); } @@ -598,12 +740,16 @@ mod tests { fn cancel_named_scheduling_works_with_normal_cancel() { new_test_ext().execute_with(|| { // at #4. - Scheduler::do_schedule_named(1u32.encode(), 4, None, 127, Call::Logger(logger::Call::log(69, 1000))).unwrap(); - let i = Scheduler::do_schedule(4, None, 127, Call::Logger(logger::Call::log(42, 1000))).unwrap(); + Scheduler::do_schedule_named( + 1u32.encode(), 4, None, 127, root(), Call::Logger(logger::Call::log(69, 1000)) + ).unwrap(); + let i = Scheduler::do_schedule( + 4, None, 127, root(), Call::Logger(logger::Call::log(42, 1000)) + ).unwrap(); run_to_block(3); assert!(logger::log().is_empty()); - assert_ok!(Scheduler::do_cancel_named(1u32.encode())); - assert_ok!(Scheduler::do_cancel(i)); + assert_ok!(Scheduler::do_cancel_named(None, 1u32.encode())); + assert_ok!(Scheduler::do_cancel(None, i)); run_to_block(100); assert!(logger::log().is_empty()); }); @@ -613,53 +759,71 @@ mod tests { fn cancel_named_periodic_scheduling_works() { new_test_ext().execute_with(|| { // at #4, every 3 blocks, 3 times. - Scheduler::do_schedule_named(1u32.encode(), 4, Some((3, 3)), 127, Call::Logger(logger::Call::log(42, 1000))).unwrap(); + Scheduler::do_schedule_named( + 1u32.encode(), 4, Some((3, 3)), 127, root(), Call::Logger(logger::Call::log(42, 1000)) + ).unwrap(); // same id results in error. - assert!(Scheduler::do_schedule_named(1u32.encode(), 4, None, 127, Call::Logger(logger::Call::log(69, 1000))).is_err()); + assert!(Scheduler::do_schedule_named( + 1u32.encode(), 4, None, 127, root(), Call::Logger(logger::Call::log(69, 1000)) + ).is_err()); // different id is ok. - Scheduler::do_schedule_named(2u32.encode(), 8, None, 127, Call::Logger(logger::Call::log(69, 1000))).unwrap(); + Scheduler::do_schedule_named( + 2u32.encode(), 8, None, 127, root(), Call::Logger(logger::Call::log(69, 1000)) + ).unwrap(); run_to_block(3); assert!(logger::log().is_empty()); run_to_block(4); - assert_eq!(logger::log(), vec![42u32]); + assert_eq!(logger::log(), vec![(root(), 42u32)]); run_to_block(6); - assert_ok!(Scheduler::do_cancel_named(1u32.encode())); + assert_ok!(Scheduler::do_cancel_named(None, 1u32.encode())); run_to_block(100); - assert_eq!(logger::log(), vec![42u32, 69u32]); + assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 69u32)]); }); } #[test] fn scheduler_respects_weight_limits() { new_test_ext().execute_with(|| { - let _ = Scheduler::do_schedule(4, None, 127, Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 2))); - let _ = Scheduler::do_schedule(4, None, 127, Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2))); + let _ = Scheduler::do_schedule( + 4, None, 127, root(), Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 2)) + ); + let _ = Scheduler::do_schedule( + 4, None, 127, root(), Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2)) + ); // 69 and 42 do not fit together run_to_block(4); - assert_eq!(logger::log(), vec![42u32]); + assert_eq!(logger::log(), vec![(root(), 42u32)]); run_to_block(5); - assert_eq!(logger::log(), vec![42u32, 69u32]); + assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 69u32)]); }); } #[test] fn scheduler_respects_hard_deadlines_more() { new_test_ext().execute_with(|| { - let _ = Scheduler::do_schedule(4, None, 0, Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 2))); - let _ = Scheduler::do_schedule(4, None, 0, Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2))); + let _ = Scheduler::do_schedule( + 4, None, 0, root(), Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 2)) + ); + let _ = Scheduler::do_schedule( + 4, None, 0, root(), Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2)) + ); // With base weights, 69 and 42 should not fit together, but do because of hard deadlines run_to_block(4); - assert_eq!(logger::log(), vec![42u32, 69u32]); + assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 69u32)]); }); } #[test] fn scheduler_respects_priority_ordering() { new_test_ext().execute_with(|| { - let _ = Scheduler::do_schedule(4, None, 1, Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 2))); - let _ = Scheduler::do_schedule(4, None, 0, Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2))); + let _ = Scheduler::do_schedule( + 4, None, 1, root(), Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 2)) + ); + let _ = Scheduler::do_schedule( + 4, None, 0, root(), Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2)) + ); run_to_block(4); - assert_eq!(logger::log(), vec![69u32, 42u32]); + assert_eq!(logger::log(), vec![(root(), 69u32), (root(), 42u32)]); }); } @@ -667,30 +831,21 @@ mod tests { fn scheduler_respects_priority_ordering_with_soft_deadlines() { new_test_ext().execute_with(|| { let _ = Scheduler::do_schedule( - 4, - None, - 255, - Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 3)), + 4, None, 255, root(), Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 3)) ); let _ = Scheduler::do_schedule( - 4, - None, - 127, - Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2)), + 4, None, 127, root(), Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2)) ); let _ = Scheduler::do_schedule( - 4, - None, - 126, - Call::Logger(logger::Call::log(2600, MaximumSchedulerWeight::get() / 2)), + 4, None, 126, root(), Call::Logger(logger::Call::log(2600, MaximumSchedulerWeight::get() / 2)) ); // 2600 does not fit with 69 or 42, but has higher priority, so will go through run_to_block(4); - assert_eq!(logger::log(), vec![2600u32]); + assert_eq!(logger::log(), vec![(root(), 2600u32)]); // 69 and 42 fit together run_to_block(5); - assert_eq!(logger::log(), vec![2600u32, 69u32, 42u32]); + assert_eq!(logger::log(), vec![(root(), 2600u32), (root(), 69u32), (root(), 42u32)]); }); } @@ -703,47 +858,45 @@ mod tests { let periodic_multiplier = ::DbWeight::get().reads_writes(1, 1); // Named - assert_ok!(Scheduler::do_schedule_named(1u32.encode(), 1, None, 255, Call::Logger(logger::Call::log(3, MaximumSchedulerWeight::get() / 3)))); + assert_ok!( + Scheduler::do_schedule_named( + 1u32.encode(), 1, None, 255, root(), + Call::Logger(logger::Call::log(3, MaximumSchedulerWeight::get() / 3)) + ) + ); // Anon Periodic let _ = Scheduler::do_schedule( - 1, - Some((1000, 3)), - 128, - Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 3)), + 1, Some((1000, 3)), 128, root(), Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 3)) ); // Anon let _ = Scheduler::do_schedule( - 1, - None, - 127, - Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2)), + 1, None, 127, root(), Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2)) ); // Named Periodic assert_ok!(Scheduler::do_schedule_named( - 2u32.encode(), - 1, - Some((1000, 3)), - 126, - Call::Logger(logger::Call::log(2600, MaximumSchedulerWeight::get() / 2)), - )); + 2u32.encode(), 1, Some((1000, 3)), 126, root(), + Call::Logger(logger::Call::log(2600, MaximumSchedulerWeight::get() / 2))) + ); // Will include the named periodic only let actual_weight = Scheduler::on_initialize(1); let call_weight = MaximumSchedulerWeight::get() / 2; - assert_eq!(actual_weight, call_weight + base_weight + base_multiplier + named_multiplier + periodic_multiplier); - assert_eq!(logger::log(), vec![2600u32]); + assert_eq!( + actual_weight, call_weight + base_weight + base_multiplier + named_multiplier + periodic_multiplier + ); + assert_eq!(logger::log(), vec![(root(), 2600u32)]); // Will include anon and anon periodic let actual_weight = Scheduler::on_initialize(2); let call_weight = MaximumSchedulerWeight::get() / 2 + MaximumSchedulerWeight::get() / 3; assert_eq!(actual_weight, call_weight + base_weight + base_multiplier * 2 + periodic_multiplier); - assert_eq!(logger::log(), vec![2600u32, 69u32, 42u32]); + assert_eq!(logger::log(), vec![(root(), 2600u32), (root(), 69u32), (root(), 42u32)]); // Will include named only let actual_weight = Scheduler::on_initialize(3); let call_weight = MaximumSchedulerWeight::get() / 3; assert_eq!(actual_weight, call_weight + base_weight + base_multiplier + named_multiplier); - assert_eq!(logger::log(), vec![2600u32, 69u32, 42u32, 3u32]); + assert_eq!(logger::log(), vec![(root(), 2600u32), (root(), 69u32), (root(), 42u32), (root(), 3u32)]); // Will contain none let actual_weight = Scheduler::on_initialize(4); @@ -794,4 +947,169 @@ mod tests { ); }); } + + #[test] + fn should_use_orign() { + new_test_ext().execute_with(|| { + let call = Box::new(Call::Logger(logger::Call::log(69, 1000))); + let call2 = Box::new(Call::Logger(logger::Call::log(42, 1000))); + assert_ok!( + Scheduler::schedule_named(system::RawOrigin::Signed(1).into(), 1u32.encode(), 4, None, 127, call) + ); + assert_ok!(Scheduler::schedule(system::RawOrigin::Signed(1).into(), 4, None, 127, call2)); + run_to_block(3); + // Scheduled calls are in the agenda. + assert_eq!(Agenda::::get(4).len(), 2); + assert!(logger::log().is_empty()); + assert_ok!(Scheduler::cancel_named(system::RawOrigin::Signed(1).into(), 1u32.encode())); + assert_ok!(Scheduler::cancel(system::RawOrigin::Signed(1).into(), 4, 1)); + // Scheduled calls are made NONE, so should not effect state + run_to_block(100); + assert!(logger::log().is_empty()); + }); + } + + #[test] + fn should_check_orign() { + new_test_ext().execute_with(|| { + let call = Box::new(Call::Logger(logger::Call::log(69, 1000))); + let call2 = Box::new(Call::Logger(logger::Call::log(42, 1000))); + assert_noop!( + Scheduler::schedule_named(system::RawOrigin::Signed(2).into(), 1u32.encode(), 4, None, 127, call), + BadOrigin + ); + assert_noop!(Scheduler::schedule(system::RawOrigin::Signed(2).into(), 4, None, 127, call2), BadOrigin); + }); + } + + #[test] + fn should_check_orign_for_cancel() { + new_test_ext().execute_with(|| { + let call = Box::new(Call::Logger(logger::Call::log_without_filter(69, 1000))); + let call2 = Box::new(Call::Logger(logger::Call::log_without_filter(42, 1000))); + assert_ok!( + Scheduler::schedule_named(system::RawOrigin::Signed(1).into(), 1u32.encode(), 4, None, 127, call) + ); + assert_ok!(Scheduler::schedule(system::RawOrigin::Signed(1).into(), 4, None, 127, call2)); + run_to_block(3); + // Scheduled calls are in the agenda. + assert_eq!(Agenda::::get(4).len(), 2); + assert!(logger::log().is_empty()); + assert_noop!(Scheduler::cancel_named(system::RawOrigin::Signed(2).into(), 1u32.encode()), BadOrigin); + assert_noop!(Scheduler::cancel(system::RawOrigin::Signed(2).into(), 4, 1), BadOrigin); + assert_noop!(Scheduler::cancel_named(system::RawOrigin::Root.into(), 1u32.encode()), BadOrigin); + assert_noop!(Scheduler::cancel(system::RawOrigin::Root.into(), 4, 1), BadOrigin); + run_to_block(5); + assert_eq!( + logger::log(), + vec![(system::RawOrigin::Signed(1).into(), 69u32), (system::RawOrigin::Signed(1).into(), 42u32)] + ); + }); + } + + #[test] + fn migration_to_v2_works() { + use substrate_test_utils::assert_eq_uvec; + + new_test_ext().execute_with(|| { + for i in 0..3u64 { + let k = i.twox_64_concat(); + let old = vec![ + Some(ScheduledV1 { + maybe_id: None, + priority: i as u8 + 10, + call: Call::Logger(logger::Call::log(96, 100)), + maybe_periodic: None, + }), + None, + Some(ScheduledV1 { + maybe_id: Some(b"test".to_vec()), + priority: 123, + call: Call::Logger(logger::Call::log(69, 1000)), + maybe_periodic: Some((456u64, 10)), + }), + ]; + frame_support::migration::put_storage_value( + b"Scheduler", + b"Agenda", + &k, + old, + ); + } + + assert_eq!(StorageVersion::get(), Releases::V1); + + assert!(Scheduler::migrate_v1_to_t2()); + + assert_eq_uvec!(Agenda::::iter().collect::>(), vec![ + ( + 0, + vec![ + Some(ScheduledV2 { + maybe_id: None, + priority: 10, + call: Call::Logger(logger::Call::log(96, 100)), + maybe_periodic: None, + origin: root(), + _phantom: PhantomData::::default(), + }), + None, + Some(ScheduledV2 { + maybe_id: Some(b"test".to_vec()), + priority: 123, + call: Call::Logger(logger::Call::log(69, 1000)), + maybe_periodic: Some((456u64, 10)), + origin: root(), + _phantom: PhantomData::::default(), + }), + ]), + ( + 1, + vec![ + Some(ScheduledV2 { + maybe_id: None, + priority: 11, + call: Call::Logger(logger::Call::log(96, 100)), + maybe_periodic: None, + origin: root(), + _phantom: PhantomData::::default(), + }), + None, + Some(ScheduledV2 { + maybe_id: Some(b"test".to_vec()), + priority: 123, + call: Call::Logger(logger::Call::log(69, 1000)), + maybe_periodic: Some((456u64, 10)), + origin: root(), + _phantom: PhantomData::::default(), + }), + ] + ), + ( + 2, + vec![ + Some(ScheduledV2 { + maybe_id: None, + priority: 12, + call: Call::Logger(logger::Call::log(96, 100)), + maybe_periodic: None, + origin: root(), + _phantom: PhantomData::::default(), + }), + None, + Some(ScheduledV2 { + maybe_id: Some(b"test".to_vec()), + priority: 123, + call: Call::Logger(logger::Call::log(69, 1000)), + maybe_periodic: Some((456u64, 10)), + origin: root(), + _phantom: PhantomData::::default(), + }), + ] + ) + ]); + + assert_eq!(StorageVersion::get(), Releases::V2); + }); + } } diff --git a/frame/support/src/dispatch.rs b/frame/support/src/dispatch.rs index d9a3561802c8f..56aaed083605f 100644 --- a/frame/support/src/dispatch.rs +++ b/frame/support/src/dispatch.rs @@ -2310,6 +2310,8 @@ mod tests { } pub mod system { + use codec::{Encode, Decode}; + pub trait Trait { type AccountId; type Call; @@ -2317,7 +2319,7 @@ mod tests { type Origin: crate::traits::OriginTrait; } - #[derive(Clone, PartialEq, Eq, Debug)] + #[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)] pub enum RawOrigin { Root, Signed(AccountId), diff --git a/frame/support/src/metadata.rs b/frame/support/src/metadata.rs index d6ec9a7373952..dca365ff8c9d1 100644 --- a/frame/support/src/metadata.rs +++ b/frame/support/src/metadata.rs @@ -316,7 +316,7 @@ mod tests { } ); - #[derive(Clone, PartialEq, Eq, Debug)] + #[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)] pub enum RawOrigin { Root, Signed(AccountId), diff --git a/frame/support/src/origin.rs b/frame/support/src/origin.rs index 77fe86cc5575b..ba9af6c98243a 100644 --- a/frame/support/src/origin.rs +++ b/frame/support/src/origin.rs @@ -222,10 +222,14 @@ macro_rules! impl_outer_origin { fn filter_call(&self, call: &Self::Call) -> bool { (self.filter)(call) } + + fn caller(&self) -> &Self::PalletsOrigin { + &self.caller + } } $crate::paste::item! { - #[derive(Clone, PartialEq, Eq, $crate::RuntimeDebug)] + #[derive(Clone, PartialEq, Eq, $crate::RuntimeDebug, $crate::codec::Encode, $crate::codec::Decode)] $(#[$attr])* #[allow(non_camel_case_types)] pub enum $caller_name { @@ -255,13 +259,25 @@ macro_rules! impl_outer_origin { } } + impl From<$system::Origin<$runtime>> for $caller_name { + fn from(x: $system::Origin<$runtime>) -> Self { + $caller_name::system(x) + } + } impl From<$system::Origin<$runtime>> for $name { /// Convert to runtime origin: /// * root origin is built with no filter /// * others use `frame-system::Trait::BaseCallFilter` fn from(x: $system::Origin<$runtime>) -> Self { + let o: $caller_name = x.into(); + o.into() + } + } + + impl From<$caller_name> for $name { + fn from(x: $caller_name) -> Self { let mut o = $name { - caller: $caller_name::system(x), + caller: x, filter: $crate::sp_std::rc::Rc::new(Box::new(|_| true)), }; @@ -273,6 +289,7 @@ macro_rules! impl_outer_origin { o } } + impl Into<$crate::sp_std::result::Result<$system::Origin<$runtime>, $name>> for $name { /// NOTE: converting to pallet origin loses the origin filter information. fn into(self) -> $crate::sp_std::result::Result<$system::Origin<$runtime>, Self> { @@ -290,17 +307,20 @@ macro_rules! impl_outer_origin { <$system::Origin<$runtime>>::from(x).into() } } + $( $crate::paste::item! { + impl From<$module::Origin < $( $generic )? $(, $module::$generic_instance )? > > for $caller_name { + fn from(x: $module::Origin < $( $generic )? $(, $module::$generic_instance )? >) -> Self { + $caller_name::[< $module $( _ $generic_instance )? >](x) + } + } + impl From<$module::Origin < $( $generic )? $(, $module::$generic_instance )? > > for $name { /// Convert to runtime origin using `frame-system::Trait::BaseCallFilter`. fn from(x: $module::Origin < $( $generic )? $(, $module::$generic_instance )? >) -> Self { - let mut o = $name { - caller: $caller_name::[< $module $( _ $generic_instance )? >](x), - filter: $crate::sp_std::rc::Rc::new(Box::new(|_| true)), - }; - $crate::traits::OriginTrait::reset_filter(&mut o); - o + let x: $caller_name = x.into(); + x.into() } } impl Into< @@ -328,15 +348,18 @@ macro_rules! impl_outer_origin { #[cfg(test)] mod tests { + use codec::{Encode, Decode}; use crate::traits::{Filter, OriginTrait}; mod system { + use super::*; + pub trait Trait { type AccountId; type Call; type BaseCallFilter; } - #[derive(Clone, PartialEq, Eq, Debug)] + #[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)] pub enum RawOrigin { Root, Signed(AccountId), @@ -356,18 +379,22 @@ mod tests { } mod origin_without_generic { - #[derive(Clone, PartialEq, Eq, Debug)] + use super::*; + + #[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)] pub struct Origin; } mod origin_with_generic { - #[derive(Clone, PartialEq, Eq, Debug)] + use super::*; + + #[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)] pub struct Origin { t: T } } - #[derive(Clone, PartialEq, Eq, Debug)] + #[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)] pub struct TestRuntime; pub struct BaseCallFilter; diff --git a/frame/support/src/traits.rs b/frame/support/src/traits.rs index f25ff67efbfb8..fbc11a0c4a5b0 100644 --- a/frame/support/src/traits.rs +++ b/frame/support/src/traits.rs @@ -1488,7 +1488,7 @@ pub mod schedule { pub const LOWEST_PRIORITY: Priority = 255; /// A type that can be used as a scheduler. - pub trait Anon { + pub trait Anon { /// An address which can be used for removing a scheduled task. type Address: Codec + Clone + Eq + EncodeLike + Debug; @@ -1501,6 +1501,7 @@ pub mod schedule { when: BlockNumber, maybe_periodic: Option>, priority: Priority, + origin: Origin, call: Call ) -> Result; @@ -1518,7 +1519,7 @@ pub mod schedule { } /// A type that can be used as a scheduler. - pub trait Named { + pub trait Named { /// An address which can be used for removing a scheduled task. type Address: Codec + Clone + Eq + EncodeLike + sp_std::fmt::Debug; @@ -1530,6 +1531,7 @@ pub mod schedule { when: BlockNumber, maybe_periodic: Option>, priority: Priority, + origin: Origin, call: Call ) -> Result; @@ -1593,6 +1595,9 @@ pub trait OriginTrait: Sized { /// Filter the call, if false then call is filtered out. fn filter_call(&self, call: &Self::Call) -> bool; + + /// Get the caller. + fn caller(&self) -> &Self::PalletsOrigin; } /// Trait to be used when types are exactly same. diff --git a/frame/support/test/tests/instance.rs b/frame/support/test/tests/instance.rs index 920554346f73e..dde2e0ca9f690 100644 --- a/frame/support/test/tests/instance.rs +++ b/frame/support/test/tests/instance.rs @@ -17,6 +17,7 @@ #![recursion_limit="128"] +use codec::{Codec, EncodeLike, Encode, Decode}; use sp_runtime::{generic, BuildStorage, traits::{BlakeTwo256, Block as _, Verify}}; use frame_support::{ Parameter, traits::Get, parameter_types, @@ -44,7 +45,7 @@ mod module1 { type Event: From> + Into<::Event>; type Origin: From>; type SomeParameter: Get; - type GenericType: Default + Clone + codec::Codec + codec::EncodeLike; + type GenericType: Default + Clone + Codec + EncodeLike; } frame_support::decl_module! { @@ -87,7 +88,7 @@ mod module1 { } } - #[derive(PartialEq, Eq, Clone, sp_runtime::RuntimeDebug)] + #[derive(PartialEq, Eq, Clone, sp_runtime::RuntimeDebug, Encode, Decode)] pub enum Origin, I> where T::BlockNumber: From { Members(u32), _Phantom(std::marker::PhantomData<(T, I)>), @@ -148,7 +149,7 @@ mod module2 { } } - #[derive(PartialEq, Eq, Clone, sp_runtime::RuntimeDebug)] + #[derive(PartialEq, Eq, Clone, sp_runtime::RuntimeDebug, Encode, Decode)] pub enum Origin, I=DefaultInstance> { Members(u32), _Phantom(std::marker::PhantomData<(T, I)>), diff --git a/frame/support/test/tests/system.rs b/frame/support/test/tests/system.rs index 0d6a22fd1a3e1..8ca2e97789d54 100644 --- a/frame/support/test/tests/system.rs +++ b/frame/support/test/tests/system.rs @@ -57,7 +57,7 @@ frame_support::decl_error! { } /// Origin for the system module. -#[derive(PartialEq, Eq, Clone, sp_runtime::RuntimeDebug)] +#[derive(PartialEq, Eq, Clone, sp_runtime::RuntimeDebug, Encode, Decode)] pub enum RawOrigin { Root, Signed(AccountId), diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index dc103b204d93f..3536d6fc719ca 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -301,7 +301,7 @@ pub struct EventRecord { } /// Origin for the System module. -#[derive(PartialEq, Eq, Clone, RuntimeDebug)] +#[derive(PartialEq, Eq, Clone, RuntimeDebug, Encode, Decode)] pub enum RawOrigin { /// The system itself ordained this dispatch to happen: this is the highest privilege level. Root,