Module forks

Source
Expand description

Fork-local utilities for pallet offchain workers (OCWs).

This module does not provide canonical chain selection or consensus.

Instead, it runs on top of a pallet’s OCW execution and maintains a deterministic local fork graph so the pallet can answer:

"what was my local state on this branch?"

A Branch represents a continuous stream of blocks extending on top of each other along the same path.

A -> B -> C -> D
same branch

As long as blocks continue as direct children, the same branch is reused and only the branch head moves forward.

When a division occurs (a sibling block appears at the same ancestry), a new fork branch is created.

A -> B -> C
        |-- D
        |-- D'

Here:

  • D is the original branch continuation
  • D' is the new sibling branch
  • A -> B -> C is the parent branch of both paths

Each branch carries its own fork-local scope through ForkScopes.

When a new sibling branch is created, it inherits from the parent branch using Accrete:

scope(D') = scope(C).accrete()

This allows each fork path to maintain isolated local state while still preserving inherited lineage state.

§Usage

Every pallet using this system should begin its OCW execution with ForksHandler::start:

fn offchain_worker(block_number: BlockNumberFor<T>) {
    <Self as ForksHandler<T, MyForkScope>>::start(
        Some("my-pallet"),
        Some(LogFormatter::default()),
        || {
            // pallet-specific OCW logic here
        }
    );
}

ForksHandler::start handles:

  • longest-chain extension vs sibling fork creation
  • missing branch recovery (during client inactivity)
  • safe branch resolution before OCW logic executes

The OCW closure (main logic) runs only after branch state is valid and ready to map the fork graph.

Since fork resolution is delayed by one block:

block N executes
-> block N - 1 is resolved and persisted

the current executing block is not yet inserted into the local fork graph.

For normal OCW access, use ForksHandler::get_prev_block_branch to retrieve the previous block’s (N - 1) resolved branch.

From that branch, navigation can continue using ForkAction and ForksHandler::transition, or helpers like:

This allows movement across:

  • parent branches
  • child branches
  • sibling branches
  • root ancestry

This provides deterministic branch-local traversal without relying on canonical consensus routing.

The system is intentionally fork-aware, scope-first, and best-effort: it tracks local execution branches, not global consensus finality.

Structs§

Branch
Fork branch details pertaining to a block and the specialized scope state defined by the pallet/module for which the local fork graph exists.

Enums§

ForkAction
Deterministic branch traversal and navigation actions for moving across the local fork graph.

Constants§

HEAD_BLOCK
Highest known longest-chain head used for fork detection.

Traits§

ForkScopes
A scope is an abstract area for storing branch-local state and anything logically related to that fork, such as:
ForksHandler
Fork-aware local branch collection framework for pallet/module state.

Functions§

block_key 🔒
Block routing identity key
branch_key 🔒
Deterministic branch identity key
divider_key 🔒
Divider identity key
load_value 🔒
Load and decode a value from Persistent Node-Local storage.
make_hash 🔒
Deterministic storage hash builder.
store_encoded 🔒
Persist an encoded value into Persistent Node-Local storage.