🪝 FRAME/Pallet Hooks 🪝
Hooks: All In One
- Onchain / STF
on_runtime_upgrade
on_initialize
poll
(WIP)on_finalize
on_idle
- Offchain:
genesis_build
offchain_worker
integrity_test
try_state
Hooks: All In One
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn on_runtime_upgrade() -> Weight {}
fn on_initialize() -> Weight {}
fn on_finalize() {}
fn on_idle(remaining_weight: Weight) -> Weight {}
fn offchain_worker() {}
fn integrity_test() {}
#[cfg(feature = "try-runtime")]
fn try_state() -> Result<(), &'static str> {}
}
#[pallet::genesis_build]
impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
fn build(&self) {}
}
Hooks: on_runtime_upgrade
- Called every time the
spec_version
/spec_name
is bumped. - Why would might you be interested in implementing this?
Hooks: on_initialize
- Useful for any kind of automatic operation.
- The weight you return is interpreted as
DispatchClass::Mandatory
.
Hooks: On_Initialize
Mandatory
Hooks should really be lightweight and predictable, with a bounded complexity.
fn on_initialize() -> Weight {
// any user can create one entry in `MyMap` 😱🔫.
<MyMap<T>>::iter().for_each(do_stuff);
}
Hooks: On_Initialize
- Question: If you have 3 pallets, in which order their
on_initialize
are called? - Question: If your runtime panics
on_initialize
, how can you recover from it? - Question: If your
on_initialize
consumes more than the maximum block weight?
Hooks: on_finalize
- Extension of
on_initialize
, but at the end of the block. - Its weight needs to be known in advance. Therefore, less preferred compared to
on_initialize
.
fn on_finalize() {} // ✅
fn on_finalize() -> Weight {} // ❌
- Nothing to do with finality in the consensus context.
Hooks: on_finalize
Generally, avoid using it unless if something REALLY needs to be happen at the end of the block.
Hooks: poll
- The non-mandatory version of
on_initialize
. - In the making 👷
Hooks: on_idle
- Optional variant of
on_finalize
, also executed at the end of the block. - Small semantical difference: executes one pallet's hook, per block, randomly, rather than all pallets'.
The Future: Moving Away From Mandatory Hooks
on_initialize
->poll
on_finalize
->on_idle
- New primitives for multi-block migrations
- New primitives for optional service work via extrinsics.
Recap: Onchain/STF Hooks
Hooks: genesis_build
- Means for each pallet to specify a f(input):state at genesis.
- This is called only once, by the client, when you create a new chain.
- Is this invoked every time you run
cargo run
?
- Is this invoked every time you run
#[pallet::genesis_build]
.
Hooks: genesis_build
#[pallet::genesis_build]
pub struct GenesisConfig<T: Config> {
pub foo: Option<u32>,
pub bar: Vec<u8>,
}
impl<T: Config> Default for GenesisConfig<T> {
fn default() -> Self {
// snip
}
}
#[pallet::genesis_build]
impl<T: Config> GenesisBuild<T> for GenesisConfig<T> {
fn build(&self) {
// use self.foo, self.bar etc.
}
}
Hooks: genesis_build
GenesisConfig
is a composite/amalgamated item at the top level runtime.
construct_runtime!(
pub enum Runtime where {
System: frame_system,
Balances: pallet_balances,
}
);
struct RuntimeGenesisConfig {
SystemConfig: pallet_system::GenesisConfig,
PalletAConfig: pallet_a::GenesisConfig,
}
Hooks: genesis_build
- Recent changes moving
genesis_build
to be used over a runtime API, rather than native runtime. #[cfg(feature = "std")]
in pallets will go away.
Hooks: offchain_worker
Fully offchain application:
- Read chain state via RPC.
- submit desired side effects back to the chain as transactions.
Runtime Offchain Worker:
- Code lives onchain, upgradable only in synchrony with the whole runtime 👎
- Ergonomic and fast state access 👍
- State writes are ignored 🤷
- Can submit transactions back to the chain as well ✅
- Source of many confusions!
Hooks: offchain_worker
- Execution entirely up to the client.
- Has a totally separate thread pool than the normal execution.
--offchain-worker <ENABLED>
Possible values:
- always:
- never:
- when-authority
--execution-offchain-worker <STRATEGY>
Possible values:
- native:
- wasm:
- both:
- native-else-wasm:
Hooks: offchain_worker
- Threads can overlap, each is reading the state of its corresponding block
Hooks: offchain_worker
Offchain workers have their own special host functions: http, dedicated storage, time, etc.
Offchain workers have the same execution limits as Wasm (limited memory, custom allocator).
Source of confusion, why OCWs cannot write to state.
Hooks: integrity_test
- Put into a test by
construct_runtime!
.
__construct_runtime_integrity_test::runtime_integrity_tests
fn integrity_test() {
assert!(
T::MyConfig::get() > 0,
"Are all of the generic types I have sensible?"
);
// notice that this is for tests, std is available.
assert!(std::mem::size_of::<T::Balance>() > 4);
}
Hooks: try_state
- A means for you to ensure correctness of your STF, after each transition.
- Entirely offchain, custom runtime-apis, conditional
compilation.
- Called from
try-runtime-cli
, which you will learn about next week, or anyone else
- Called from
- Examples from your assignment?
Hooks: Recap
- What other hooks can you think of?
Additional Resources! 😋
Check speaker notes (click "s" 😉)