Expand description
Learn about the offchain workers, how they function, and how to use them, as provided by the
frame
APIs.
§Offchain Workers
This reference document explains how offchain workers work in Substrate and FRAME. The main
focus is upon FRAME’s implementation of this functionality. Nonetheless, offchain workers are a
Substrate-provided feature and can be used with possible alternatives to frame
as well.
Offchain workers are a commonly misunderstood topic, therefore we explain them bottom-up, starting at the fundamentals and then describing the developer interface.
§Context
Recall from crate::reference_docs::wasm_meta_protocol
that the node and the runtime
communicate with one another via host functions and runtime APIs. Many of these interactions
contribute to the actual state transition of the blockchain. For example sp_api::Core
is the
main runtime API that is called to execute new blocks.
Offchain workers are in principle not different in any way: It is a runtime API exposed by the
wasm blob (sp_offchain::OffchainWorkerApi
), and the node software calls into it when it
deems fit. But, crucially, this API call is different in that:
- It can have no impact on the state ie. it is OFF (the) CHAIN. If any state is altered during the execution of this API call, it is discarded.
- It has access to an extended set of host functions that allow the wasm blob to do more. For example, call into HTTP requests.
The main way through which an offchain worker can interact with the state is by submitting an extrinsic to the chain. This is the ONLY way to alter the state from an offchain worker.
pallet_example_offchain_worker
provides an example of this.
Given the “Off Chain” nature of this API, it is important to remember that calling this API is entirely optional. Some nodes might call into it, some might not, and it would have no impact on the execution of your blockchain because no state is altered no matter the execution of the offchain worker API.
Substrate’s CLI allows some degree of configuration about this, allowing node operators to
specify when they want to run the offchain worker API. See
sc_cli::RunCmd::offchain_worker_params
.
§Nondeterministic Execution
Needless to say, given the above description, the code in your offchain worker API can be
nondeterministic, as it is not part of the blockchain’s STF, so it can be executed at unknown
times, by unknown nodes, and has no impact on the state. This is why an HTTP
(sp_runtime::offchain::http
) API is readily provided to the offchain worker APIs. Because
there is no need for determinism in this context.
A common mistake here is for novice developers to see this HTTP API, and imagine that
polkadot-sdk
somehow magically solved the determinism in blockchains, and now a blockchain can make HTTP calls and it will all work. This is absolutely NOT the case. An HTTP call made by the offchain worker is non-deterministic by design. Blockchains can’t and always won’t be able to perform non-deterministic operations such as making HTTP calls to a foreign server.
§FRAME’s API
frame
provides a simple API through which pallets can define offchain worker functions. This
is part of frame::traits::Hooks
, which is implemented as a part of
frame::pallet_macros::hooks
.
#[frame::pallet]
pub mod pallet {
use frame::prelude::*;
#[pallet::config]
pub trait Config: frame_system::Config {}
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn offchain_worker(block_number: BlockNumberFor<T>) {
// ...
}
}
}
Additionally, sp_runtime::offchain
provides a set of utilities that can be used to moderate
the execution of offchain workers.
§Think Twice: Why Use Substrate’s Offchain Workers?
Consider the fact that in principle, an offchain worker code written using the above API is no different than an equivalent written with an actual offchain interaction library, such as Polkadot-JS, or any of the other ones listed here.
They can both read from the state, and have no means of updating the state, other than the route of submitting an extrinsic to the chain. Therefore, it is worth thinking twice before embedding a logic as a part of Substrate’s offchain worker API. Does it have to be there? Can it not be a simple, actual offchain application that lives outside of the chain’s WASM blob?
Some of the reasons why you might want to do the opposite, and actually embed an offchain worker API into the WASM blob are:
- Accessing the state is easier within the
offchain_worker
function, as it is already a part of the runtime, andframe::pallet_macros::storage
provides all the tools needed to read the state. Other client libraries might provide varying degrees of capability here. - It will be updated in synchrony with the runtime. A Substrate’s offchain application is part of the same WASM blob, and is therefore guaranteed to be up to date.
For example, imagine you have modified a storage item to have a new type. This will possibly
require a crate::reference_docs::frame_runtime_upgrades_and_migrations
, and any offchain
code, such as a Polkadot-JS application, will have to be updated to reflect this change. Whereas
the WASM offchain worker code is guaranteed to already be updated, or else the runtime code will
not even compile.