diff --git a/frame/utility/src/benchmarking.rs b/frame/utility/src/benchmarking.rs index 78911fd310e85..8b7add6081834 100644 --- a/frame/utility/src/benchmarking.rs +++ b/frame/utility/src/benchmarking.rs @@ -86,5 +86,9 @@ benchmarks! { assert_last_event::(Event::BatchCompleted.into()) } + downgrade_class { + let call = Box::new(frame_system::Call::set_code_without_checks { code: vec![] }.into()); + }: _(RawOrigin::Root, call) + impl_benchmark_test_suite!(Pallet, crate::tests::new_test_ext(), crate::tests::Test); } diff --git a/frame/utility/src/lib.rs b/frame/utility/src/lib.rs index af212a31eb971..78346d6f8fe2e 100644 --- a/frame/utility/src/lib.rs +++ b/frame/utility/src/lib.rs @@ -489,6 +489,29 @@ pub mod pallet { let res = call.dispatch_bypass_filter(frame_system::RawOrigin::Root.into()); res.map(|_| ()).map_err(|e| e.error) } + + /// Dispatch a function call with normal class. + /// + /// This function sets the dispatch class of the call to normal by force. + /// + /// The dispatch origin for this call must be _Root_. + #[pallet::call_index(6)] + #[pallet::weight({ + let dispatch_info = call.get_dispatch_info(); + ( + T::WeightInfo::downgrade_class() + .saturating_add(dispatch_info.weight), + DispatchClass::Normal + ) + })] + pub fn downgrade_class( + origin: OriginFor, + call: Box<::RuntimeCall>, + ) -> DispatchResult { + ensure_root(origin)?; + let res = call.dispatch_bypass_filter(frame_system::RawOrigin::Root.into()); + res.map(|_| ()).map_err(|e| e.error) + } } } diff --git a/frame/utility/src/tests.rs b/frame/utility/src/tests.rs index d23c57e69bec5..fedde833c2ddd 100644 --- a/frame/utility/src/tests.rs +++ b/frame/utility/src/tests.rs @@ -24,7 +24,7 @@ use super::*; use crate as utility; use frame_support::{ assert_err_ignore_postinfo, assert_noop, assert_ok, - dispatch::{DispatchError, DispatchErrorWithPostInfo, Dispatchable, Pays}, + dispatch::{DispatchClass, DispatchError, DispatchErrorWithPostInfo, Dispatchable, Pays}, error::BadOrigin, parameter_types, storage, traits::{ConstU32, ConstU64, Contains, GenesisBuild}, @@ -937,3 +937,22 @@ fn with_weight_works() { ); }) } + +#[test] +fn downgrade_class_works() { + new_test_ext().execute_with(|| { + let upgrade_code_call = + Box::new(RuntimeCall::System(frame_system::Call::set_code_without_checks { + code: vec![], + })); + // Class before is `Operational`. + assert_eq!(upgrade_code_call.get_dispatch_info().class, DispatchClass::Operational); + + let with_weight_call = Call::::downgrade_class { call: upgrade_code_call }; + // Class after is `Normal`. + assert_eq!( + with_weight_call.get_dispatch_info().class, + frame_support::dispatch::DispatchClass::Normal + ); + }) +} diff --git a/frame/utility/src/weights.rs b/frame/utility/src/weights.rs index 0ff261a33f362..bf7980e132f90 100644 --- a/frame/utility/src/weights.rs +++ b/frame/utility/src/weights.rs @@ -53,6 +53,7 @@ pub trait WeightInfo { fn batch_all(c: u32, ) -> Weight; fn dispatch_as() -> Weight; fn force_batch(c: u32, ) -> Weight; + fn downgrade_class() -> Weight; } /// Weights for pallet_utility using the Substrate node and recommended hardware. @@ -102,6 +103,13 @@ impl WeightInfo for SubstrateWeight { // Standard Error: 2_222 .saturating_add(Weight::from_parts(4_272_019, 0).saturating_mul(c.into())) } + fn downgrade_class() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 9_811_000 picoseconds. + Weight::from_parts(10_162_000, 0) + } } // For backwards compatibility and tests @@ -150,4 +158,11 @@ impl WeightInfo for () { // Standard Error: 2_222 .saturating_add(Weight::from_parts(4_272_019, 0).saturating_mul(c.into())) } + fn downgrade_class() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 9_811_000 picoseconds. + Weight::from_parts(10_162_000, 0) + } }