diff --git a/frame/executive/src/lib.rs b/frame/executive/src/lib.rs index 9485e75bbdecf..8f07aafbab27e 100644 --- a/frame/executive/src/lib.rs +++ b/frame/executive/src/lib.rs @@ -119,7 +119,7 @@ use sp_std::{prelude::*, marker::PhantomData}; use frame_support::{ weights::{GetDispatchInfo, DispatchInfo, DispatchClass}, - traits::{OnInitialize, OnFinalize, OnRuntimeUpgrade, OffchainWorker, ExecuteBlock}, + traits::{OnInitialize, OnIdle, OnFinalize, OnRuntimeUpgrade, OffchainWorker, ExecuteBlock}, dispatch::PostDispatchInfo, }; use sp_runtime::{ @@ -160,6 +160,7 @@ impl< AllModules: OnRuntimeUpgrade + OnInitialize + + OnIdle + OnFinalize + OffchainWorker, COnRuntimeUpgrade: OnRuntimeUpgrade, @@ -186,6 +187,7 @@ impl< UnsignedValidator, AllModules: OnRuntimeUpgrade + OnInitialize + + OnIdle + OnFinalize + OffchainWorker, COnRuntimeUpgrade: OnRuntimeUpgrade, @@ -349,8 +351,8 @@ where // post-extrinsics book-keeping >::note_finished_extrinsics(); - as OnFinalize>::on_finalize(block_number); - >::on_finalize(block_number); + + Self::idle_and_finalize_hook(block_number); } /// Finalize the block - it is up the caller to ensure that all header fields are valid @@ -360,12 +362,36 @@ where sp_tracing::enter_span!( sp_tracing::Level::TRACE, "finalize_block" ); >::note_finished_extrinsics(); let block_number = >::block_number(); - as OnFinalize>::on_finalize(block_number); - >::on_finalize(block_number); + + Self::idle_and_finalize_hook(block_number); >::finalize() } + fn idle_and_finalize_hook(block_number: NumberFor) { + let weight = >::block_weight(); + let max_weight = >::get().max_block; + let mut remaining_weight = max_weight.saturating_sub(weight.total()); + + if remaining_weight > 0 { + let mut used_weight = + as OnIdle>::on_idle( + block_number, + remaining_weight + ); + remaining_weight = remaining_weight.saturating_sub(used_weight); + used_weight = >::on_idle( + block_number, + remaining_weight + ) + .saturating_add(used_weight); + >::register_extra_weight_unchecked(used_weight, DispatchClass::Mandatory); + } + + as OnFinalize>::on_finalize(block_number); + >::on_finalize(block_number); + } + /// Apply extrinsic outside of the block execution function. /// /// This doesn't attempt to validate anything regarding the block, but it builds a list of uxt @@ -555,6 +581,11 @@ mod tests { 175 } + fn on_idle(n: T::BlockNumber, remaining_weight: Weight) -> Weight { + println!("on_idle{}, {})", n, remaining_weight); + 175 + } + fn on_finalize() { println!("on_finalize(?)"); } @@ -769,7 +800,7 @@ mod tests { header: Header { parent_hash: [69u8; 32].into(), number: 1, - state_root: hex!("2c01e6f33d595793119823478b45b36978a8f65a731b5ae3fdfb6330b4cd4b11").into(), + state_root: hex!("6e70de4fa07bac443dc7f8a812c8a0c941aacfa892bb373c5899f7d511d4c25b").into(), extrinsics_root: hex!("03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314").into(), digest: Digest { logs: vec![], }, }, @@ -1006,10 +1037,11 @@ mod tests { new_test_ext(1).execute_with(|| { Executive::initialize_block(&Header::new_from_number(1)); + Executive::finalize_block(); // NOTE: might need updates over time if new weights are introduced. // For now it only accounts for the base block execution weight and // the `on_initialize` weight defined in the custom test module. - assert_eq!(>::block_weight().total(), 175 + 10); + assert_eq!(>::block_weight().total(), 175 + 175 + 10); }) } diff --git a/frame/support/procedural/src/pallet/expand/hooks.rs b/frame/support/procedural/src/pallet/expand/hooks.rs index d55a74209d053..3976f2c602dde 100644 --- a/frame/support/procedural/src/pallet/expand/hooks.rs +++ b/frame/support/procedural/src/pallet/expand/hooks.rs @@ -63,6 +63,22 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { } } + impl<#type_impl_gen> + #frame_support::traits::OnIdle<::BlockNumber> + for #pallet_ident<#type_use_gen> #where_clause + { + fn on_idle( + n: ::BlockNumber, + remaining_weight: #frame_support::weights::Weight + ) -> #frame_support::weights::Weight { + < + Self as #frame_support::traits::Hooks< + ::BlockNumber + > + >::on_idle(n, remaining_weight) + } + } + impl<#type_impl_gen> #frame_support::traits::OnInitialize<::BlockNumber> for #pallet_ident<#type_use_gen> #where_clause diff --git a/frame/support/src/dispatch.rs b/frame/support/src/dispatch.rs index 4dd2c83f1578a..64b7b7a8e2180 100644 --- a/frame/support/src/dispatch.rs +++ b/frame/support/src/dispatch.rs @@ -300,6 +300,12 @@ impl Parameter for T where T: Codec + EncodeLike + Clone + Eq + fmt::Debug {} /// * `fn on_initialize(n: BlockNumber) -> frame_support::weights::Weight` or /// * `fn on_initialize() -> frame_support::weights::Weight` /// +/// * `on_idle`: Executes at the end of a block. Passes a remaining weight to provide a threshold +/// for when to execute non vital functions. Using this function will implement the +/// [`OnIdle`](./traits/trait.OnIdle.html) trait. +/// Function signature is: +/// * `fn on_idle(n: BlockNumber, remaining_weight: Weight) -> frame_support::weights::Weight` +/// /// * `on_finalize`: Executes at the end of a block. Using this function will /// implement the [`OnFinalize`](./traits/trait.OnFinalize.html) trait. /// Function signature can be either: @@ -340,6 +346,7 @@ macro_rules! decl_module { {} {} {} + {} [] $($t)* ); @@ -375,6 +382,7 @@ macro_rules! decl_module { {} {} {} + {} [] $($t)* ); @@ -389,6 +397,7 @@ macro_rules! decl_module { {} { $( $on_initialize:tt )* } { $( $on_runtime_upgrade:tt )* } + { $( $on_idle:tt )* } { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } @@ -407,6 +416,7 @@ macro_rules! decl_module { { $vis fn deposit_event() = default; } { $( $on_initialize )* } { $( $on_runtime_upgrade )* } + { $( $on_idle )* } { $( $on_finalize )* } { $( $offchain )* } { $( $constants )* } @@ -424,6 +434,7 @@ macro_rules! decl_module { {} { $( $on_initialize:tt )* } { $( $on_runtime_upgrade:tt )* } + { $( $on_idle:tt )* } { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } @@ -449,6 +460,7 @@ macro_rules! decl_module { { $( $deposit_event:tt )+ } { $( $on_initialize:tt )* } { $( $on_runtime_upgrade:tt )* } + { $( $on_idle:tt )* } { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } @@ -470,6 +482,7 @@ macro_rules! decl_module { { $( $deposit_event:tt )* } { $( $on_initialize:tt )* } { $( $on_runtime_upgrade:tt )* } + { $( $on_idle:tt )* } {} { $( $offchain:tt )* } { $( $constants:tt )* } @@ -488,6 +501,7 @@ macro_rules! decl_module { { $( $deposit_event )* } { $( $on_initialize )* } { $( $on_runtime_upgrade )* } + { $( $on_idle )* } { fn on_finalize( $( $param_name : $param ),* ) { $( $impl )* } } @@ -508,6 +522,7 @@ macro_rules! decl_module { { $( $deposit_event:tt )* } { $( $on_initialize:tt )* } { $( $on_runtime_upgrade:tt )* } + { $( $on_idle:tt )* } {} { $( $offchain:tt )* } { $( $constants:tt )* } @@ -535,6 +550,7 @@ macro_rules! decl_module { { $( $deposit_event:tt )* } { $( $on_initialize:tt )* } { $( $on_runtime_upgrade:tt )* } + { $( $on_idle:tt )* } { $( $on_finalize:tt )+ } { $( $offchain:tt )* } { $( $constants:tt )* } @@ -548,6 +564,72 @@ macro_rules! decl_module { ) => { compile_error!("`on_finalize` can only be passed once as input."); }; + + // Add on_idle + (@normalize + $(#[$attr:meta])* + pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident$(, I: $instantiable:path $(= $module_default_instance:path)?)?> + for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident + { $( $other_where_bounds:tt )* } + { $( $deposit_event:tt )* } + { $( $on_initialize:tt )* } + { $( $on_runtime_upgrade:tt )* } + {} + { $( $on_finalize:tt )* } + { $( $offchain:tt )* } + { $( $constants:tt )* } + { $( $error_type:tt )* } + { $( $integrity_test:tt )* } + [ $( $dispatchables:tt )* ] + $(#[doc = $doc_attr:tt])* + fn on_idle($param_name1:ident : $param1:ty, $param_name2:ident: $param2:ty $(,)? ) -> $return:ty { $( $impl:tt )* } + $($rest:tt)* + ) => { + $crate::decl_module!(@normalize + $(#[$attr])* + pub struct $mod_type<$trait_instance: $trait_name$(, I: $instantiable $(= $module_default_instance)?)?> + for enum $call_type where origin: $origin_type, system = $system + { $( $other_where_bounds )* } + { $( $deposit_event )* } + { $( $on_initialize )* } + { $( $on_runtime_upgrade )* } + { + fn on_idle( $param_name1: $param1, $param_name2: $param2 ) -> $return { $( $impl )* } + } + { $( $on_finalize:tt )* } + { $( $offchain )* } + { $( $constants )* } + { $( $error_type )* } + { $( $integrity_test)* } + [ $( $dispatchables )* ] + $($rest)* + ); + }; + // compile_error for invalid on_idle function signature in decl_module + (@normalize + $(#[$attr:meta])* + pub struct $mod_type:ident< + $trait_instance:ident: $trait_name:ident$(, I: $instantiable:path $(= $module_default_instance:path)?)? + > + for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident + { $( $other_where_bounds:tt )* } + { $( $deposit_event:tt )* } + { $( $on_initialize:tt )* } + { $( $on_runtime_upgrade:tt )* } + { $( $on_finalize:tt )* } + { $( $offchain:tt )* } + { $( $constants:tt )* } + { $( $error_type:tt )* } + { $( $integrity_test:tt )* } + [ $( $dispatchables:tt )* ] + $(#[doc = $doc_attr:tt])* + $(#[weight = $weight:expr])? + fn on_idle + $($rest:tt)* + ) => { + compile_error!("`on_idle` method is reserved and syntax doesn't match expected syntax."); + }; + // compile_error on_runtime_upgrade, without a given weight removed syntax. (@normalize $(#[$attr:meta])* @@ -559,6 +641,7 @@ macro_rules! decl_module { { $( $deposit_event:tt )* } { $( $on_initialize:tt )* } {} + { $( $on_idle:tt )* } { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } @@ -584,6 +667,7 @@ macro_rules! decl_module { { $( $deposit_event:tt )* } { $( $on_initialize:tt )* } {} + { $( $on_idle:tt )* } { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } @@ -611,6 +695,7 @@ macro_rules! decl_module { { $( $deposit_event:tt )* } { $( $on_initialize:tt )* } {} + { $( $on_idle:tt )* } { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } @@ -631,6 +716,7 @@ macro_rules! decl_module { { fn on_runtime_upgrade( $( $param_name : $param ),* ) -> $return { $( $impl )* } } + { $( $on_idle )* } { $( $on_finalize )* } { $( $offchain )* } { $( $constants )* } @@ -651,6 +737,7 @@ macro_rules! decl_module { { $( $deposit_event:tt )* } { $( $on_initialize:tt )* } { $( $on_runtime_upgrade:tt )+ } + { $( $on_idle:tt )* } { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } @@ -674,6 +761,7 @@ macro_rules! decl_module { { $( $deposit_event:tt )* } { $( $on_initialize:tt )* } { $( $on_runtime_upgrade:tt )* } + { $( $on_idle:tt )* } { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } @@ -692,6 +780,7 @@ macro_rules! decl_module { { $( $deposit_event )* } { $( $on_initialize )* } { $( $on_runtime_upgrade )* } + { $( $on_idle )* } { $( $on_finalize )* } { $( $offchain )* } { $( $constants )* } @@ -715,6 +804,7 @@ macro_rules! decl_module { { $( $deposit_event:tt )* } { $( $on_initialize:tt )* } { $( $on_runtime_upgrade:tt )* } + { $( $on_idle:tt )* } { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } @@ -738,6 +828,7 @@ macro_rules! decl_module { { $( $deposit_event:tt )* } {} { $( $on_runtime_upgrade:tt )* } + { $( $on_idle:tt )* } { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } @@ -763,6 +854,7 @@ macro_rules! decl_module { { $( $deposit_event:tt )* } {} { $( $on_runtime_upgrade:tt )* } + { $( $on_idle:tt )* } { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } @@ -790,6 +882,7 @@ macro_rules! decl_module { { $( $deposit_event:tt )* } {} { $( $on_runtime_upgrade:tt )* } + { $( $on_idle:tt )* } { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } @@ -810,6 +903,7 @@ macro_rules! decl_module { fn on_initialize( $( $param_name : $param ),* ) -> $return { $( $impl )* } } { $( $on_runtime_upgrade )* } + { $( $on_idle )* } { $( $on_finalize )* } { $( $offchain )* } { $( $constants )* } @@ -830,6 +924,7 @@ macro_rules! decl_module { { $( $deposit_event:tt )* } { $( $on_initialize:tt )+ } { $( $on_runtime_upgrade:tt )* } + { $( $on_idle:tt )* } { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } @@ -853,6 +948,7 @@ macro_rules! decl_module { { $( $deposit_event:tt )* } { $( $on_initialize:tt )* } { $( $on_runtime_upgrade:tt )* } + { $( $on_idle:tt )* } { $( $on_finalize:tt )* } { } { $( $constants:tt )* } @@ -873,6 +969,7 @@ macro_rules! decl_module { { $( $deposit_event )* } { $( $on_initialize )* } { $( $on_runtime_upgrade )* } + { $( $on_idle )* } { $( $on_finalize )* } { fn offchain_worker( $( $param_name : $param ),* ) { $( $impl )* } } { $( $constants )* } @@ -893,6 +990,7 @@ macro_rules! decl_module { { $( $deposit_event:tt )* } { $( $on_initialize:tt )* } { $( $on_runtime_upgrade:tt )* } + { $( $on_idle:tt )* } { $( $on_finalize:tt )* } { $( $offchain:tt )+ } { $( $constants:tt )* } @@ -917,6 +1015,7 @@ macro_rules! decl_module { { $( $deposit_event:tt )* } { $( $on_initialize:tt )* } { $( $on_runtime_upgrade:tt )* } + { $( $on_idle:tt )* } { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } @@ -938,6 +1037,7 @@ macro_rules! decl_module { { $( $deposit_event )* } { $( $on_initialize )* } { $( $on_runtime_upgrade )* } + { $( $on_idle )* } { $( $on_finalize )* } { $( $offchain )* } { @@ -964,6 +1064,7 @@ macro_rules! decl_module { { $( $deposit_event:tt )* } { $( $on_initialize:tt )* } { $( $on_runtime_upgrade:tt )* } + { $( $on_idle:tt )* } { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } @@ -984,6 +1085,7 @@ macro_rules! decl_module { { $( $deposit_event )* } { $( $on_initialize )* } { $( $on_runtime_upgrade )* } + { $( $on_idle )* } { $( $on_finalize )* } { $( $offchain )* } { $( $constants )* } @@ -1005,6 +1107,7 @@ macro_rules! decl_module { { $( $deposit_event:tt )* } { $( $on_initialize:tt )* } { $( $on_runtime_upgrade:tt )* } + { $( $on_idle:tt )* } { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } @@ -1023,6 +1126,7 @@ macro_rules! decl_module { { $( $deposit_event )* } { $( $on_initialize )* } { $( $on_runtime_upgrade )* } + { $( $on_idle )* } { $( $on_finalize )* } { $( $offchain )* } { $( $constants )* } @@ -1045,6 +1149,7 @@ macro_rules! decl_module { { $( $deposit_event:tt )* } { $( $on_initialize:tt )* } { $( $on_runtime_upgrade:tt )* } + { $( $on_idle:tt )* } { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } @@ -1069,6 +1174,7 @@ macro_rules! decl_module { { $( $deposit_event )* } { $( $on_initialize )* } { $( $on_runtime_upgrade )* } + { $( $on_idle )* } { $( $on_finalize )* } { $( $offchain )* } { $( $constants )* } @@ -1099,6 +1205,7 @@ macro_rules! decl_module { { $( $deposit_event:tt )* } { $( $on_initialize:tt )* } { $( $on_runtime_upgrade:tt )* } + { $( $on_idle:tt )* } { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } @@ -1127,6 +1234,7 @@ macro_rules! decl_module { { $( $deposit_event:tt )* } { $( $on_initialize:tt )* } { $( $on_runtime_upgrade:tt )* } + { $( $on_idle:tt )* } { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } @@ -1155,6 +1263,7 @@ macro_rules! decl_module { { $( $deposit_event:tt )* } { $( $on_initialize:tt )* } { $( $on_runtime_upgrade:tt )* } + { $( $on_idle:tt )* } { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } @@ -1183,6 +1292,7 @@ macro_rules! decl_module { { $( $deposit_event:tt )* } { $( $on_initialize:tt )* } { $( $on_runtime_upgrade:tt )* } + { $( $on_idle:tt )* } { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } @@ -1212,6 +1322,7 @@ macro_rules! decl_module { { $( $deposit_event:tt )* } { $( $on_initialize:tt )* } { $( $on_runtime_upgrade:tt )* } + { $( $on_idle:tt )* } { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } @@ -1229,6 +1340,7 @@ macro_rules! decl_module { { $( $deposit_event )* } { $( $on_initialize )* } { $( $on_runtime_upgrade )* } + { $( $on_idle )* } { $( $on_finalize )* } { $( $offchain )* } { $( $constants )* } @@ -1485,6 +1597,35 @@ macro_rules! decl_module { } }; + (@impl_on_idle + { $system:ident } + $module:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?>; + { $( $other_where_bounds:tt )* } + fn on_idle($param1:ident : $param1_ty:ty, $param2:ident: $param2_ty:ty) -> $return:ty { $( $impl:tt )* } + ) => { + impl<$trait_instance: $system::Config + $trait_name$(, $instance: $instantiable)?> + $crate::traits::OnIdle<<$trait_instance as $system::Config>::BlockNumber> + for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )* + { + fn on_idle($param1: $param1_ty, $param2: $param2_ty) -> $return { + $crate::sp_tracing::enter_span!($crate::sp_tracing::trace_span!("on_idle")); + { $( $impl )* } + } + } + }; + + (@impl_on_idle + { $system:ident } + $module:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?>; + { $( $other_where_bounds:tt )* } + ) => { + impl<$trait_instance: $system::Config + $trait_name$(, $instance: $instantiable)?> + $crate::traits::OnIdle<<$trait_instance as $system::Config>::BlockNumber> + for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )* + { + } + }; + (@impl_offchain { $system:ident } $module:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?>; @@ -1700,6 +1841,7 @@ macro_rules! decl_module { { $( $deposit_event:tt )* } { $( $on_initialize:tt )* } { $( $on_runtime_upgrade:tt )* } + { $( $on_idle:tt )* } { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } @@ -1741,6 +1883,14 @@ macro_rules! decl_module { $( $on_finalize )* } + $crate::decl_module! { + @impl_on_idle + { $system } + $mod_type<$trait_instance: $trait_name $(, $instance: $instantiable)?>; + { $( $other_where_bounds )* } + $( $on_idle )* + } + $crate::decl_module! { @impl_offchain { $system } @@ -2415,6 +2565,9 @@ macro_rules! __check_reserved_fn_name { (on_runtime_upgrade $( $rest:ident )*) => { $crate::__check_reserved_fn_name!(@compile_error on_runtime_upgrade); }; + (on_idle $( $rest:ident )*) => { + $crate::__check_reserved_fn_name!(@compile_error on_idle); + }; (on_finalize $( $rest:ident )*) => { $crate::__check_reserved_fn_name!(@compile_error on_finalize); }; @@ -2459,7 +2612,7 @@ mod tests { use super::*; use crate::weights::{DispatchInfo, DispatchClass, Pays, RuntimeDbWeight}; use crate::traits::{ - CallMetadata, GetCallMetadata, GetCallName, OnInitialize, OnFinalize, OnRuntimeUpgrade, + CallMetadata, GetCallMetadata, GetCallName, OnInitialize, OnFinalize, OnIdle, OnRuntimeUpgrade, IntegrityTest, Get, PalletInfo, }; @@ -2522,6 +2675,10 @@ mod tests { fn operational(_origin) { unreachable!() } fn on_initialize(n: T::BlockNumber,) -> Weight { if n.into() == 42 { panic!("on_initialize") } 7 } + fn on_idle(n: T::BlockNumber, remaining_weight: Weight,) -> Weight { + if n.into() == 42 || remaining_weight == 42 { panic!("on_idle") } + 7 + } fn on_finalize(n: T::BlockNumber,) { if n.into() == 42 { panic!("on_finalize") } } fn on_runtime_upgrade() -> Weight { 10 } fn offchain_worker() {} @@ -2687,6 +2844,23 @@ mod tests { assert_eq!( as OnInitialize>::on_initialize(10), 7); } + #[test] + #[should_panic(expected = "on_idle")] + fn on_idle_should_work_1() { + as OnIdle>::on_idle(42, 9); + } + + #[test] + #[should_panic(expected = "on_idle")] + fn on_idle_should_work_2() { + as OnIdle>::on_idle(9, 42); + } + + #[test] + fn on_idle_should_work_3() { + assert_eq!( as OnIdle>::on_idle(10, 11), 7); + } + #[test] #[should_panic(expected = "on_finalize")] fn on_finalize_should_work() { diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 3c3fc20a530d1..a06fd7a1d9b92 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -1223,7 +1223,7 @@ pub mod pallet_prelude { /// /// ### Macro expansion: /// -/// The macro implements the traits `OnInitialize`, `OnFinalize`, `OnRuntimeUpgrade`, +/// The macro implements the traits `OnInitialize`, `OnIdle`, `OnFinalize`, `OnRuntimeUpgrade`, /// `OffchainWorker`, `IntegrityTest` using `Hooks` implementation. /// /// NOTE: OnRuntimeUpgrade is implemented with `Hooks::on_runtime_upgrade` and some additional diff --git a/frame/support/src/traits.rs b/frame/support/src/traits.rs index 3d103ef04c2d3..30337c4ddb6c5 100644 --- a/frame/support/src/traits.rs +++ b/frame/support/src/traits.rs @@ -1518,6 +1518,38 @@ pub trait OnFinalize { fn on_finalize(_n: BlockNumber) {} } +/// The block's on idle trait. +/// +/// Implementing this lets you express what should happen for your pallet before +/// block finalization (see `on_finalize` hook) in case any remaining weight is left. +pub trait OnIdle { + /// The block is being finalized. + /// Implement to have something happen in case there is leftover weight. + /// Check the passed `remaining_weight` to make sure it is high enough to allow for + /// your pallet's extra computation. + /// + /// NOTE: This function is called AFTER ALL extrinsics - including inherent extrinsics - + /// in a block are applied but before `on_finalize` is executed. + fn on_idle( + _n: BlockNumber, + _remaining_weight: crate::weights::Weight + ) -> crate::weights::Weight { + 0 + } +} + +#[impl_for_tuples(30)] +impl OnIdle for Tuple { + fn on_idle(n: BlockNumber, remaining_weight: crate::weights::Weight) -> crate::weights::Weight { + let mut weight = 0; + for_tuples!( #( + let adjusted_remaining_weight = remaining_weight.saturating_sub(weight); + weight = weight.saturating_add(Tuple::on_idle(n.clone(), adjusted_remaining_weight)); + )* ); + weight + } +} + /// The block initialization trait. /// /// Implementing this lets you express what should happen for your pallet when the block is @@ -1535,9 +1567,9 @@ pub trait OnInitialize { #[impl_for_tuples(30)] impl OnInitialize for Tuple { - fn on_initialize(_n: BlockNumber) -> crate::weights::Weight { + fn on_initialize(n: BlockNumber) -> crate::weights::Weight { let mut weight = 0; - for_tuples!( #( weight = weight.saturating_add(Tuple::on_initialize(_n.clone())); )* ); + for_tuples!( #( weight = weight.saturating_add(Tuple::on_initialize(n.clone())); )* ); weight } } @@ -2035,6 +2067,18 @@ pub trait Hooks { /// The block is being finalized. Implement to have something happen. fn on_finalize(_n: BlockNumber) {} + /// This will be run when the block is being finalized (before `on_finalize`). + /// Implement to have something happen using the remaining weight. + /// Will not fire if the remaining weight is 0. + /// Return the weight used, the hook will subtract it from current weight used + /// and pass the result to the next `on_idle` hook if it exists. + fn on_idle( + _n: BlockNumber, + _remaining_weight: crate::weights::Weight + ) -> crate::weights::Weight { + 0 + } + /// The block is being initialized. Implement to have something happen. /// /// Return the non-negotiable weight consumed in the block.