diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index b8d2c51e27..a67f4855ac 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -175,14 +175,14 @@ impl pallet_utility::Trait for Runtime { } parameter_types! { - pub const MaximumWeight: Weight = 2_000_000; + pub const MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * MaximumBlockWeight::get(); } impl pallet_scheduler::Trait for Runtime { type Event = Event; type Origin = Origin; type Call = Call; - type MaximumWeight = MaximumWeight; + type MaximumWeight = MaximumSchedulerWeight; } parameter_types! { diff --git a/substrate/frame/democracy/src/lib.rs b/substrate/frame/democracy/src/lib.rs index df1ea0d531..a182907aba 100644 --- a/substrate/frame/democracy/src/lib.rs +++ b/substrate/frame/democracy/src/lib.rs @@ -1252,7 +1252,7 @@ decl_module! { } /// Enact a proposal from a referendum. For now we just make the weight be the maximum. - #[weight = frame_system::Module::::max_extrinsic_weight(DispatchClass::Normal)] + #[weight = T::MaximumBlockWeight::get()] fn enact_proposal(origin, proposal_hash: T::Hash, index: ReferendumIndex) -> DispatchResult { ensure_root(origin)?; Self::do_enact_proposal(proposal_hash, index) diff --git a/substrate/frame/democracy/src/tests.rs b/substrate/frame/democracy/src/tests.rs index 076eb66d6b..a835a0ff6e 100644 --- a/substrate/frame/democracy/src/tests.rs +++ b/substrate/frame/democracy/src/tests.rs @@ -77,7 +77,7 @@ impl_outer_event! { pub struct Test; parameter_types! { pub const BlockHashCount: u64 = 250; - pub const MaximumBlockWeight: Weight = 1024; + pub const MaximumBlockWeight: Weight = 1_000_000; pub const MaximumBlockLength: u32 = 2 * 1024; pub const AvailableBlockRatio: Perbill = Perbill::one(); } @@ -106,14 +106,16 @@ impl frame_system::Trait for Test { type OnKilledAccount = (); } parameter_types! { - pub const ExistentialDeposit: u64 = 1; - pub const MaximumWeight: u32 = 1000000; + pub const MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * MaximumBlockWeight::get(); } impl pallet_scheduler::Trait for Test { type Event = Event; type Origin = Origin; type Call = Call; - type MaximumWeight = MaximumWeight; + type MaximumWeight = MaximumSchedulerWeight; +} +parameter_types! { + pub const ExistentialDeposit: u64 = 1; } impl pallet_balances::Trait for Test { type Balance = u64; diff --git a/substrate/frame/scheduler/src/lib.rs b/substrate/frame/scheduler/src/lib.rs index 87b5aafdf3..4859507378 100644 --- a/substrate/frame/scheduler/src/lib.rs +++ b/substrate/frame/scheduler/src/lib.rs @@ -513,11 +513,14 @@ mod tests { impl logger::Trait for Test { type Event = (); } + parameter_types! { + pub const MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * MaximumBlockWeight::get(); + } impl Trait for Test { type Event = (); type Origin = Origin; type Call = Call; - type MaximumWeight = MaximumBlockWeight; + type MaximumWeight = MaximumSchedulerWeight; } type System = system::Module; type Logger = logger::Module; @@ -611,8 +614,8 @@ mod tests { #[test] fn scheduler_respects_weight_limits() { new_test_ext().execute_with(|| { - Scheduler::do_schedule(4, None, 127, Call::Logger(logger::Call::log(42, MaximumBlockWeight::get() / 2))); - Scheduler::do_schedule(4, None, 127, Call::Logger(logger::Call::log(69, MaximumBlockWeight::get() / 2))); + Scheduler::do_schedule(4, None, 127, Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 2))); + Scheduler::do_schedule(4, None, 127, 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]); @@ -624,8 +627,8 @@ mod tests { #[test] fn scheduler_respects_hard_deadlines_more() { new_test_ext().execute_with(|| { - Scheduler::do_schedule(4, None, 0, Call::Logger(logger::Call::log(42, MaximumBlockWeight::get() / 2))); - Scheduler::do_schedule(4, None, 0, Call::Logger(logger::Call::log(69, MaximumBlockWeight::get() / 2))); + Scheduler::do_schedule(4, None, 0, Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 2))); + Scheduler::do_schedule(4, None, 0, 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]); @@ -635,8 +638,8 @@ mod tests { #[test] fn scheduler_respects_priority_ordering() { new_test_ext().execute_with(|| { - Scheduler::do_schedule(4, None, 1, Call::Logger(logger::Call::log(42, MaximumBlockWeight::get() / 2))); - Scheduler::do_schedule(4, None, 0, Call::Logger(logger::Call::log(69, MaximumBlockWeight::get() / 2))); + Scheduler::do_schedule(4, None, 1, Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 2))); + Scheduler::do_schedule(4, None, 0, Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2))); run_to_block(4); assert_eq!(logger::log(), vec![69u32, 42u32]); }); @@ -645,9 +648,9 @@ mod tests { #[test] fn scheduler_respects_priority_ordering_with_soft_deadlines() { new_test_ext().execute_with(|| { - Scheduler::do_schedule(4, None, 255, Call::Logger(logger::Call::log(42, MaximumBlockWeight::get() / 3))); - Scheduler::do_schedule(4, None, 127, Call::Logger(logger::Call::log(69, MaximumBlockWeight::get() / 2))); - Scheduler::do_schedule(4, None, 126, Call::Logger(logger::Call::log(2600, MaximumBlockWeight::get() / 2))); + Scheduler::do_schedule(4, None, 255, Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 3))); + Scheduler::do_schedule(4, None, 127, Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2))); + Scheduler::do_schedule(4, None, 126, 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); @@ -667,29 +670,29 @@ 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, MaximumBlockWeight::get() / 3)))); + assert_ok!(Scheduler::do_schedule_named(1u32.encode(), 1, None, 255, Call::Logger(logger::Call::log(3, MaximumSchedulerWeight::get() / 3)))); // Anon Periodic - Scheduler::do_schedule(1, Some((1000, 3)), 128, Call::Logger(logger::Call::log(42, MaximumBlockWeight::get() / 3))); + Scheduler::do_schedule(1, Some((1000, 3)), 128, Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 3))); // Anon - Scheduler::do_schedule(1, None, 127, Call::Logger(logger::Call::log(69, MaximumBlockWeight::get() / 2))); + Scheduler::do_schedule(1, None, 127, 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, MaximumBlockWeight::get() / 2)))); + assert_ok!(Scheduler::do_schedule_named(2u32.encode(), 1, Some((1000, 3)), 126, 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 = MaximumBlockWeight::get() / 2; + 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]); // Will include anon and anon periodic let actual_weight = Scheduler::on_initialize(2); - let call_weight = MaximumBlockWeight::get() / 2 + MaximumBlockWeight::get() / 3; + 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]); // Will include named only let actual_weight = Scheduler::on_initialize(3); - let call_weight = MaximumBlockWeight::get() / 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]); diff --git a/substrate/frame/sudo/src/lib.rs b/substrate/frame/sudo/src/lib.rs index d19c92358c..2e7a53140f 100644 --- a/substrate/frame/sudo/src/lib.rs +++ b/substrate/frame/sudo/src/lib.rs @@ -92,7 +92,7 @@ use sp_runtime::{DispatchResult, traits::{StaticLookup, Dispatchable}}; use frame_support::{ Parameter, decl_module, decl_event, decl_storage, decl_error, ensure, }; -use frame_support::weights::{GetDispatchInfo, FunctionOf, Pays}; +use frame_support::weights::{Weight, GetDispatchInfo, FunctionOf, Pays}; use frame_system::{self as system, ensure_signed}; pub trait Trait: frame_system::Trait { @@ -134,6 +134,30 @@ decl_module! { Self::deposit_event(RawEvent::Sudid(res.map(|_| ()).map_err(|e| e.error))); } + /// Authenticates the sudo key and dispatches a function call with `Root` origin. + /// This function does not check the weight of the call, and instead allows the + /// Sudo user to specify the weight of the call. + /// + /// The dispatch origin for this call must be _Signed_. + /// + /// # + /// - O(1). + /// - The weight of this call is defined by the caller. + /// # + #[weight = FunctionOf( + |(_, &weight): (&Box<::Call>,&Weight,)| weight, + |(call, _): (&Box<::Call>,&Weight,)| call.get_dispatch_info().class, + Pays::Yes, + )] + fn sudo_unchecked_weight(origin, call: Box<::Call>, _weight: Weight) { + // This is a public call, so we ensure that the origin is some signed account. + let sender = ensure_signed(origin)?; + ensure!(sender == Self::key(), Error::::RequireSudo); + + let res = call.dispatch(frame_system::RawOrigin::Root.into()); + Self::deposit_event(RawEvent::Sudid(res.map(|_| ()).map_err(|e| e.error))); + } + /// Authenticates the current sudo key and sets the given AccountId (`new`) as the new sudo key. /// /// The dispatch origin for this call must be _Signed_. diff --git a/substrate/frame/system/benchmarking/src/lib.rs b/substrate/frame/system/benchmarking/src/lib.rs index b9b9619c09..22e6dba842 100644 --- a/substrate/frame/system/benchmarking/src/lib.rs +++ b/substrate/frame/system/benchmarking/src/lib.rs @@ -134,7 +134,7 @@ benchmarks! { let value = storage::unhashed::get_raw(&last_key).ok_or("No value stored")?; assert_eq!(value, last_key); - }: _(RawOrigin::Root, prefix) + }: _(RawOrigin::Root, prefix, p) verify { assert_eq!(storage::unhashed::get_raw(&last_key), None); } diff --git a/substrate/frame/system/src/lib.rs b/substrate/frame/system/src/lib.rs index eca1b3291e..8346b727b2 100644 --- a/substrate/frame/system/src/lib.rs +++ b/substrate/frame/system/src/lib.rs @@ -512,8 +512,10 @@ decl_module! { /// /// # /// - `O(1)` + /// - Base Weight: 0.665 µs, independent of remark length. + /// - No DB operations. /// # - #[weight = 0] + #[weight = 700_000] fn remark(origin, _remark: Vec) { ensure_signed(origin)?; } @@ -523,8 +525,10 @@ decl_module! { /// # /// - `O(1)` /// - 1 storage write. + /// - Base Weight: 1.405 µs + /// - 1 write to HEAP_PAGES /// # - #[weight = (0, DispatchClass::Operational)] + #[weight = (T::DbWeight::get().writes(1) + 1_500_000, DispatchClass::Operational)] fn set_heap_pages(origin, pages: u64) { ensure_root(origin)?; storage::unhashed::put_raw(well_known_keys::HEAP_PAGES, &pages.encode()); @@ -537,8 +541,10 @@ decl_module! { /// - 1 storage write (codec `O(C)`). /// - 1 call to `can_set_code`: `O(S)` (calls `sp_io::misc::runtime_version` which is expensive). /// - 1 event. + /// The weight of this function is dependent on the runtime, but generally this is very expensive. + /// We will treat this as a full block. /// # - #[weight = (200_000_000, DispatchClass::Operational)] + #[weight = (T::MaximumBlockWeight::get(), DispatchClass::Operational)] pub fn set_code(origin, code: Vec) { Self::can_set_code(origin, &code)?; @@ -552,8 +558,9 @@ decl_module! { /// - `O(C)` where `C` length of `code` /// - 1 storage write (codec `O(C)`). /// - 1 event. + /// The weight of this function is dependent on the runtime. We will treat this as a full block. /// # - #[weight = (200_000_000, DispatchClass::Operational)] + #[weight = (T::MaximumBlockWeight::get(), DispatchClass::Operational)] pub fn set_code_without_checks(origin, code: Vec) { ensure_root(origin)?; storage::unhashed::put_raw(well_known_keys::CODE, &code); @@ -563,11 +570,14 @@ decl_module! { /// Set the new changes trie configuration. /// /// # - /// - `O(D)` where `D` length of `Digest` + /// - `O(1)` /// - 1 storage write or delete (codec `O(1)`). - /// - 1 call to `deposit_log`: `O(D)` (which depends on the length of `Digest`) + /// - 1 call to `deposit_log`: Uses `append` API, so O(1) + /// - Base Weight: 7.218 µs + /// - DB Weight: + /// - Writes: Changes Trie, System Digest /// # - #[weight = (20_000_000, DispatchClass::Operational)] + #[weight = (T::DbWeight::get().writes(2) + 10_000_000, DispatchClass::Operational)] pub fn set_changes_trie_config(origin, changes_trie_config: Option) { ensure_root(origin)?; match changes_trie_config.clone() { @@ -589,8 +599,17 @@ decl_module! { /// # /// - `O(I)` where `I` length of `items` /// - `I` storage writes (`O(1)`). + /// - Base Weight: 0.568 * i µs + /// - Writes: Number of items /// # - #[weight = (0, DispatchClass::Operational)] + #[weight = FunctionOf( + |(items,): (&Vec,)| { + T::DbWeight::get().writes(items.len() as Weight) + .saturating_add((items.len() as Weight).saturating_mul(600_000)) + }, + DispatchClass::Operational, + Pays::Yes, + )] fn set_storage(origin, items: Vec) { ensure_root(origin)?; for i in &items { @@ -601,10 +620,19 @@ decl_module! { /// Kill some items from storage. /// /// # - /// - `O(VK)` where `V` length of `keys` and `K` length of one key - /// - `V` storage deletions. + /// - `O(IK)` where `I` length of `keys` and `K` length of one key + /// - `I` storage deletions. + /// - Base Weight: .378 * i µs + /// - Writes: Number of items /// # - #[weight = (0, DispatchClass::Operational)] + #[weight = FunctionOf( + |(keys,): (&Vec,)| { + T::DbWeight::get().writes(keys.len() as Weight) + .saturating_add((keys.len() as Weight).saturating_mul(400_000)) + }, + DispatchClass::Operational, + Pays::Yes, + )] fn kill_storage(origin, keys: Vec) { ensure_root(origin)?; for key in &keys { @@ -614,12 +642,24 @@ decl_module! { /// Kill all storage items with a key that starts with the given prefix. /// + /// **NOTE:** We rely on the Root origin to provide us the number of subkeys under + /// the prefix we are removing to accurately calculate the weight of this function. + /// /// # /// - `O(P)` where `P` amount of keys with prefix `prefix` /// - `P` storage deletions. + /// - Base Weight: 0.834 * P µs + /// - Writes: Number of subkeys + 1 /// # - #[weight = (0, DispatchClass::Operational)] - fn kill_prefix(origin, prefix: Key) { + #[weight = FunctionOf( + |(_, &subkeys): (&Key, &u32)| { + T::DbWeight::get().writes(Weight::from(subkeys) + 1) + .saturating_add((Weight::from(subkeys) + 1).saturating_mul(850_000)) + }, + DispatchClass::Operational, + Pays::Yes, + )] + fn kill_prefix(origin, prefix: Key, _subkeys: u32) { ensure_root(origin)?; storage::unhashed::kill_prefix(&prefix); } @@ -630,8 +670,11 @@ decl_module! { /// # /// - `O(1)` /// - 1 storage read and deletion. + /// -------------------- + /// Base Weight: 8.626 µs + /// No DB Read or Write operations because caller is already in overlay /// # - #[weight = (25_000_000, DispatchClass::Operational)] + #[weight = (10_000_000, DispatchClass::Operational)] fn suicide(origin) { let who = ensure_signed(origin)?; let account = Account::::get(&who); @@ -874,23 +917,6 @@ impl Module { AllExtrinsicsWeight::get().unwrap_or_default() } - /// Get the quota ratio of each dispatch class type. This indicates that all operational and mandatory - /// dispatches can use the full capacity of any resource, while user-triggered ones can consume - /// a portion. - pub fn get_dispatch_limit_ratio(class: DispatchClass) -> Perbill { - match class { - DispatchClass::Operational | DispatchClass::Mandatory - => ::one(), - DispatchClass::Normal => T::AvailableBlockRatio::get(), - } - } - - /// The maximum weight of an allowable extrinsic. Only one of these could exist in a block. - pub fn max_extrinsic_weight(class: DispatchClass) -> Weight { - let limit = Self::get_dispatch_limit_ratio(class) * T::MaximumBlockWeight::get(); - limit - (T::BlockExecutionWeight::get() + T::ExtrinsicBaseWeight::get()) - } - pub fn all_extrinsics_len() -> u32 { AllExtrinsicsLen::get().unwrap_or_default() } @@ -1257,7 +1283,11 @@ impl CheckWeight where /// dispatches can use the full capacity of any resource, while user-triggered ones can consume /// a portion. fn get_dispatch_limit_ratio(class: DispatchClass) -> Perbill { - Module::::get_dispatch_limit_ratio(class) + match class { + DispatchClass::Operational | DispatchClass::Mandatory + => ::one(), + DispatchClass::Normal => T::AvailableBlockRatio::get(), + } } /// Checks if the current extrinsic can fit into the block with respect to block weight limits. @@ -1680,7 +1710,7 @@ pub(crate) mod tests { use sp_std::cell::RefCell; use sp_core::H256; use sp_runtime::{traits::{BlakeTwo256, IdentityLookup, SignedExtension}, testing::Header, DispatchError}; - use frame_support::{impl_outer_origin, parameter_types, assert_ok, assert_err}; + use frame_support::{impl_outer_origin, parameter_types, assert_ok}; impl_outer_origin! { pub enum Origin for Test where system = super {} @@ -2082,47 +2112,6 @@ pub(crate) mod tests { }) } - #[test] - fn max_extrinsic_weight_is_allowed_but_nothing_more() { - // Dispatch Class Normal - new_test_ext().execute_with(|| { - let one = DispatchInfo { weight: 1, ..Default::default() }; - let max = DispatchInfo { weight: System::max_extrinsic_weight(DispatchClass::Normal), ..Default::default() }; - let len = 0_usize; - - assert_ok!(CheckWeight::::do_pre_dispatch(&max, len)); - assert_err!( - CheckWeight::::do_pre_dispatch(&one, len), - InvalidTransaction::ExhaustsResources, - ); - // Weight should be 75% of 1024 (max block weight) - assert_eq!(System::all_extrinsics_weight(), 768); - }); - - // Dispatch Class Operational - new_test_ext().execute_with(|| { - let one = DispatchInfo { - weight: 1, - class: DispatchClass::Operational, - ..Default::default() - }; - let max = DispatchInfo { - weight: System::max_extrinsic_weight(DispatchClass::Operational), - class: DispatchClass::Operational, - ..Default::default() - }; - let len = 0_usize; - - assert_ok!(CheckWeight::::do_pre_dispatch(&max, len)); - assert_err!( - CheckWeight::::do_pre_dispatch(&one, len), - InvalidTransaction::ExhaustsResources, - ); - // Weight should be 100% of max block weight - assert_eq!(System::all_extrinsics_weight(), ::MaximumBlockWeight::get()); - }); - } - #[test] fn mandatory_extrinsic_doesnt_care_about_limits() { new_test_ext().execute_with(|| { @@ -2156,9 +2145,6 @@ pub(crate) mod tests { // 10 is taken for block execution weight // So normal extrinsic can be 758 weight (-5 for base extrinsic weight) // And Operational can be 256 to produce a full block (-5 for base) - - assert_eq!(System::max_extrinsic_weight(DispatchClass::Normal), 753); - let max_normal = DispatchInfo { weight: 753, ..Default::default() }; let rest_operational = DispatchInfo { weight: 251, class: DispatchClass::Operational, ..Default::default() };