improve pallet hooks docs (#14578)

* improve pallet hooks docs

* Update frame/support/src/traits/hooks.rs

Co-authored-by: Sam Johnson <sam@durosoft.com>

* Update frame/support/src/traits/hooks.rs

Co-authored-by: Sam Johnson <sam@durosoft.com>

* Update frame/support/src/traits/hooks.rs

Co-authored-by: Sam Johnson <sam@durosoft.com>

* fix mastekn removal

* Apply suggestions from code review

Co-authored-by: Juan <juangirini@gmail.com>

* add diagram

* fix all links

* fix diagram

* improve diagram with some notes

* update

---------

Co-authored-by: Sam Johnson <sam@durosoft.com>
Co-authored-by: parity-processbot <>
Co-authored-by: Juan <juangirini@gmail.com>
This commit is contained in:
Kian Paimani
2023-07-17 12:37:01 +02:00
committed by GitHub
parent 1a2d59b1af
commit 6479764161
3 changed files with 196 additions and 113 deletions
+160 -113
View File
@@ -15,7 +15,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//! Traits for hooking tasks to events in a blockchain's lifecycle.
//! Traits relating to pallet hooks.
//!
//! See [`Hooks`] as the main entry-point.
#![deny(missing_docs)]
use crate::weights::Weight;
use impl_trait_for_tuples::impl_for_tuples;
@@ -25,18 +29,9 @@ use sp_std::prelude::*;
#[cfg(feature = "try-runtime")]
use sp_runtime::TryRuntimeError;
/// The block initialization trait.
///
/// Implementing this lets you express what should happen for your pallet when the block is
/// beginning (right before the first extrinsic is executed).
/// See [`Hooks::on_initialize`].
pub trait OnInitialize<BlockNumber> {
/// The block is being initialized. Implement to have something happen.
///
/// Return the non-negotiable weight consumed in the block.
///
/// NOTE: This function is called BEFORE ANY extrinsic in a block is applied,
/// including inherent extrinsics. Hence for instance, if you runtime includes
/// `pallet_timestamp`, the `timestamp` is not yet up to date at this point.
/// See [`Hooks::on_initialize`].
fn on_initialize(_n: BlockNumber) -> Weight {
Weight::zero()
}
@@ -53,32 +48,18 @@ impl<BlockNumber: Clone> OnInitialize<BlockNumber> for Tuple {
}
}
/// The block finalization trait.
///
/// Implementing this lets you express what should happen for your pallet when the block is ending.
/// See [`Hooks::on_finalize`].
#[cfg_attr(all(not(feature = "tuples-96"), not(feature = "tuples-128")), impl_for_tuples(64))]
#[cfg_attr(all(feature = "tuples-96", not(feature = "tuples-128")), impl_for_tuples(96))]
#[cfg_attr(feature = "tuples-128", impl_for_tuples(128))]
pub trait OnFinalize<BlockNumber> {
/// The block is being finalized. Implement to have something happen.
///
/// NOTE: This function is called AFTER ALL extrinsics in a block are applied,
/// including inherent extrinsics.
/// See [`Hooks::on_finalize`].
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.
/// See [`Hooks::on_idle`].
pub trait OnIdle<BlockNumber> {
/// 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.
/// See [`Hooks::on_idle`].
fn on_idle(_n: BlockNumber, _remaining_weight: Weight) -> Weight {
Weight::zero()
}
@@ -118,26 +99,14 @@ pub trait OnGenesis {
fn on_genesis() {}
}
/// The runtime upgrade trait.
///
/// Implementing this lets you express what should happen when the runtime upgrades,
/// and changes may need to occur to your module.
/// See [`Hooks::on_runtime_upgrade`].
pub trait OnRuntimeUpgrade {
/// Perform a module upgrade.
///
/// # Warning
///
/// This function will be called before we initialized any runtime state, aka `on_initialize`
/// wasn't called yet. So, information like the block number and any other
/// block local data are not accessible.
///
/// Return the non-negotiable weight consumed for runtime upgrade.
/// See [`Hooks::on_runtime_upgrade`].
fn on_runtime_upgrade() -> Weight {
Weight::zero()
}
/// Same as `on_runtime_upgrade`, but perform the optional `pre_upgrade` and `post_upgrade` as
/// well.
/// See [`Hooks::on_runtime_upgrade`].
#[cfg(feature = "try-runtime")]
fn try_on_runtime_upgrade(checks: bool) -> Result<Weight, TryRuntimeError> {
let maybe_state = if checks {
@@ -159,31 +128,13 @@ pub trait OnRuntimeUpgrade {
Ok(weight)
}
/// Execute some pre-checks prior to a runtime upgrade.
///
/// Return a `Vec<u8>` that can contain arbitrary encoded data (usually some pre-upgrade state),
/// which will be passed to `post_upgrade` after upgrading for post-check. An empty vector
/// should be returned if there is no such need.
///
/// This hook is never meant to be executed on-chain but is meant to be used by testing tools.
///
/// This hook must not write to any state, as it would make the main `on_runtime_upgrade` path
/// inaccurate.
/// See [`Hooks::on_runtime_upgrade`].
#[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
Ok(Vec::new())
}
/// Execute some post-checks after a runtime upgrade.
///
/// The `state` parameter is the `Vec<u8>` returned by `pre_upgrade` before upgrading, which
/// can be used for post-check. NOTE: if `pre_upgrade` is not implemented an empty vector will
/// be passed in, in such case `post_upgrade` should ignore it.
///
/// This hook is never meant to be executed on-chain but is meant to be used by testing tools.
///
/// This hook must not write to any state, as it would make the main `on_runtime_upgrade` path
/// inaccurate.
/// See [`Hooks::on_runtime_upgrade`].
#[cfg(feature = "try-runtime")]
fn post_upgrade(_state: Vec<u8>) -> Result<(), TryRuntimeError> {
Ok(())
@@ -200,9 +151,9 @@ impl OnRuntimeUpgrade for Tuple {
weight
}
/// We are executing pre- and post-checks sequentially in order to be able to test several
/// consecutive migrations for the same pallet without errors. Therefore pre and post upgrade
/// hooks for tuples are a noop.
// We are executing pre- and post-checks sequentially in order to be able to test several
// consecutive migrations for the same pallet without errors. Therefore pre and post upgrade
// hooks for tuples are a noop.
#[cfg(feature = "try-runtime")]
fn try_on_runtime_upgrade(checks: bool) -> Result<Weight, TryRuntimeError> {
let mut weight = Weight::zero();
@@ -239,64 +190,150 @@ impl OnRuntimeUpgrade for Tuple {
}
}
/// Type that provide some integrity tests.
///
/// This implemented for modules by `decl_module`.
/// See [`Hooks::integrity_test`].
#[cfg_attr(all(not(feature = "tuples-96"), not(feature = "tuples-128")), impl_for_tuples(64))]
#[cfg_attr(all(feature = "tuples-96", not(feature = "tuples-128")), impl_for_tuples(96))]
#[cfg_attr(feature = "tuples-128", impl_for_tuples(128))]
pub trait IntegrityTest {
/// Run integrity test.
///
/// The test is not executed in a externalities provided environment.
/// See [`Hooks::integrity_test`].
fn integrity_test() {}
}
/// The pallet hooks trait. Implementing this lets you express some logic to execute.
#[cfg_attr(doc, aquamarine::aquamarine)]
/// The pallet hooks trait. This is merely an umbrella trait for:
///
/// - [`OnInitialize`]
/// - [`OnFinalize`]
/// - [`OnRuntimeUpgrade`]
/// - [`crate::traits::misc::OffchainWorker`]
/// - [`OnIdle`]
/// - [`IntegrityTest`]
///
/// ## Ordering
///
/// For all hooks, except [`OnIdle`] the order of execution is derived from how the pallets are
/// ordered in [`crate::construct_runtime`].
///
/// ## Summary
///
/// In short, the following diagram shows the flow of hooks in a pallet
///
/// ```mermaid
/// graph LR
/// Optional --> BeforeExtrinsics
/// BeforeExtrinsics --> Extrinsics
/// Extrinsics --> AfterExtrinsics
/// subgraph Optional
/// OnRuntimeUpgrade
/// end
///
/// subgraph BeforeExtrinsics
/// OnInitialize
/// end
///
/// subgraph Extrinsics
/// direction TB
/// Inherent1
/// Inherent2
/// Extrinsic1
/// Extrinsic2
///
/// Inherent1 --> Inherent2
/// Inherent2 --> Extrinsic1
/// Extrinsic1 --> Extrinsic2
/// end
///
/// subgraph AfterExtrinsics
/// OnIdle
/// OnFinalize
///
/// OnIdle --> OnFinalize
/// end
/// ```
///
/// * `OnRuntimeUpgrade` is only executed before everything else if a code
/// * `OnRuntimeUpgrade` is mandatorily at the beginning of the block body (extrinsics) being
/// processed. change is detected.
/// * Extrinsics start with inherents, and continue with other signed or unsigned extrinsics.
/// * `OnIdle` optionally comes after extrinsics.
/// `OnFinalize` mandatorily comes after `OnIdle`.
///
/// > `OffchainWorker` is not part of this flow, as it is not really part of the consensus/main
/// > block import path, and is called optionally, and in other circumstances. See
/// > [`crate::traits::misc::OffchainWorker`] for more information.
///
/// To learn more about the execution of hooks see `frame-executive` as this component is is charge
/// of dispatching extrinsics and placing the hooks in the correct order.
pub trait Hooks<BlockNumber> {
/// The block is being finalized. Implement to have something happen.
/// Block initialization hook. This is called at the very beginning of block execution.
///
/// Must return the non-negotiable weight of both itself and whatever [`Hooks::on_finalize`]
/// wishes to consume.
///
/// The weight returned by this is treated as `DispatchClass::Mandatory`, meaning that
/// it MUST BE EXECUTED. If this is not the case, consider using [`Hooks::on_idle`] instead.
///
/// NOTE: This function is called BEFORE ANY extrinsic in a block is applied, including inherent
/// extrinsics. Hence for instance, if you runtime includes `pallet-timestamp`, the `timestamp`
/// is not yet up to date at this point.
fn on_initialize(_n: BlockNumber) -> Weight {
Weight::zero()
}
/// Block finalization hook. This is called at the very end of block execution.
///
/// Note that this has nothing to do with finality in the "consensus" sense.
///
/// Note that the non-negotiable weight for this has must have already been returned by
/// [`Hooks::on_initialize`]. It usage alone is not permitted.
///
/// Similar to [`Hooks::on_initialize`] it should only be used when execution is absolutely
/// necessary. In other cases, consider using [`Hooks::on_idle`] instead.
fn on_finalize(_n: BlockNumber) {}
/// This will be run when the block is being finalized (before `on_finalize`).
/// Hook to consume a block's idle time. This will run when the block is being finalized (before
/// [`Hooks::on_finalize`]).
///
/// Implement to have something happen using the remaining weight. Will not fire if the
/// remaining weight is 0.
/// Given that all dispatchables are already executed and noted (and the weight for
/// [`Hooks::on_finalize`], which comes next, is also already accounted for via
/// `on_initialize`), this hook consumes anything that is leftover.
///
/// Each pallet's `on_idle` is chosen to be the first to execute in a round-robin fashion
/// indexed by the block number.
///
/// Return the weight used, the caller will use this to calculate the remaining weight and then
/// call the next pallet `on_idle` hook if there is still weight left.
///
/// Any implementation should always respect `_remaining_weight` and never consume (and
/// therefore return) more than this amount.
fn on_idle(_n: BlockNumber, _remaining_weight: Weight) -> Weight {
Weight::zero()
}
/// The block is being initialized. Implement to have something happen.
/// Hook executed when a code change (aka. a "runtime upgrade") is detected by FRAME.
///
/// Return the non-negotiable weight consumed in the block.
fn on_initialize(_n: BlockNumber) -> Weight {
Weight::zero()
}
/// Perform a module upgrade.
/// Be aware that this is called before [`Hooks::on_initialize`] of any pallet; therefore, a lot
/// of the critical storage items such as `block_number` in system pallet might have not been
/// set.
///
/// NOTE: this doesn't include all pallet logic triggered on runtime upgrade. For instance it
/// doesn't include the write of the pallet version in storage. The final complete logic
/// triggered on runtime upgrade is given by implementation of `OnRuntimeUpgrade` trait by
/// `Pallet`.
/// Vert similar to [`Hooks::on_initialize`], any code in this block is mandatory and MUST
/// execute. Use with care.
///
/// # Warning
/// ## Implementation Note: Versioning
///
/// This function will be called before we initialized any runtime state, aka `on_initialize`
/// wasn't called yet. So, information like the block number and any other block local data are
/// not accessible.
/// 1. An implementation of this should typically follow a pattern where the version of the
/// pallet is checked against the onchain version, and a decision is made about what needs to be
/// done. This is helpful to prevent accidental repetitive execution of this hook, which can be
/// catastrophic.
///
/// Return the non-negotiable weight consumed for runtime upgrade.
/// Alternatively, `migrations::VersionedRuntimeUpgrade` can be used to assist with
/// this.
///
/// While this function can be freely implemented, using `on_runtime_upgrade` from inside the
/// pallet is discouraged and might get deprecated in the future. Alternatively, export the same
/// logic as a free-function from your pallet, and pass it to `type Executive` from the
/// top-level runtime.
/// ## Implementation Note: Runtime Level Migration
///
/// Additional "upgrade hooks" can be created by pallets by a manual implementation of
/// [`Hooks::on_runtime_upgrade`] which can be passed on to `Executive` at the top level
/// runtime.
fn on_runtime_upgrade() -> Weight {
Weight::zero()
}
@@ -336,26 +373,36 @@ pub trait Hooks<BlockNumber> {
Ok(())
}
/// Implementing this function on a module allows you to perform long-running tasks
/// that make (by default) validators generate transactions that feed results
/// of those long-running computations back on chain.
/// Implementing this function on a pallet allows you to perform long-running tasks that are
/// dispatched as separate threads, and entirely independent of the main wasm runtime.
///
/// NOTE: This function runs off-chain, so it can access the block state,
/// but cannot preform any alterations. More specifically alterations are
/// not forbidden, but they are not persisted in any way after the worker
/// has finished.
/// This function can freely read from the state, but any change it makes to the state is
/// meaningless. Writes can be pushed back to the chain by submitting extrinsics from the
/// offchain worker to the transaction pool. See `pallet-example-offchain-worker` for more
/// details on this.
///
/// This function is being called after every block import (when fully synced).
/// Moreover, the code in this function has access to a wider range of host functions in
/// [`sp-io`], namely [`sp_io::offchain`]. This includes exotic operations such as HTTP calls
/// that are not really possible in the rest of the runtime code.
///
/// Implement this and use any of the `Offchain` `sp_io` set of APIs
/// to perform off-chain computations, calls and submit transactions
/// with results to trigger any on-chain changes.
/// Any state alterations are lost and are not persisted.
/// The execution of this hook is entirely optional and is left at the discretion of the
/// node-side software and its configuration. In a normal substrate-cli, look for the CLI
/// flags related to offchain-workers to learn more.
fn offchain_worker(_n: BlockNumber) {}
/// Run integrity test.
/// Check the integrity of this pallet's configuration.
///
/// The test is not executed in a externalities provided environment.
/// Any code located in this hook is placed in an auto-generated test, and generated as a part
/// of [`crate::construct_runtime`]'s expansion. Look for a test case with a name along the
/// lines of: `__construct_runtime_integrity_test`.
///
/// This hook is the location where the values/types provided to the `Config` trait
/// of the pallet can be tested for correctness. For example, if two `type Foo: Get<u32>` and
/// `type Bar: Get<u32>` where `Foo::get()` must always be greater than `Bar::get()`, such
/// checks can be asserted upon here.
///
/// Note that this hook is not executed in an externality environment, so if access to state is
/// needed, the code should be wrapped in `sp_io::TestExternalities`.
fn integrity_test() {}
}