polkadot_sdk_docs/reference_docs/frame_offchain_workers.rs
1//! # Offchain Workers
2//!
3//! This reference document explains how offchain workers work in Substrate and FRAME. The main
4//! focus is upon FRAME's implementation of this functionality. Nonetheless, offchain workers are a
5//! Substrate-provided feature and can be used with possible alternatives to [`frame`] as well.
6//!
7//! Offchain workers are a commonly misunderstood topic, therefore we explain them bottom-up,
8//! starting at the fundamentals and then describing the developer interface.
9//!
10//! ## Context
11//!
12//! Recall from [`crate::reference_docs::wasm_meta_protocol`] that the node and the runtime
13//! communicate with one another via host functions and runtime APIs. Many of these interactions
14//! contribute to the actual state transition of the blockchain. For example [`sp_api::Core`] is the
15//! main runtime API that is called to execute new blocks.
16//!
17//! Offchain workers are in principle not different in any way: It is a runtime API exposed by the
18//! wasm blob ([`sp_offchain::OffchainWorkerApi`]), and the node software calls into it when it
19//! deems fit. But, crucially, this API call is different in that:
20//!
21//! 1. It can have no impact on the state ie. it is _OFF (the) CHAIN_. If any state is altered
22//! during the execution of this API call, it is discarded.
23//! 2. It has access to an extended set of host functions that allow the wasm blob to do more. For
24//! example, call into HTTP requests.
25//!
26//! > The main way through which an offchain worker can interact with the state is by submitting an
27//! > extrinsic to the chain. This is the ONLY way to alter the state from an offchain worker.
28//! > [`pallet_example_offchain_worker`] provides an example of this.
29//!
30//!
31//! Given the "Off Chain" nature of this API, it is important to remember that calling this API is
32//! entirely optional. Some nodes might call into it, some might not, and it would have no impact on
33//! the execution of your blockchain because no state is altered no matter the execution of the
34//! offchain worker API.
35//!
36//! Substrate's CLI allows some degree of configuration about this, allowing node operators to
37//! specify when they want to run the offchain worker API. See
38//! [`sc_cli::RunCmd::offchain_worker_params`].
39//!
40//! ## Nondeterministic Execution
41//!
42//! Needless to say, given the above description, the code in your offchain worker API can be
43//! nondeterministic, as it is not part of the blockchain's STF, so it can be executed at unknown
44//! times, by unknown nodes, and has no impact on the state. This is why an HTTP
45//! ([`sp_runtime::offchain::http`]) API is readily provided to the offchain worker APIs. Because
46//! there is no need for determinism in this context.
47//!
48//! > A common mistake here is for novice developers to see this HTTP API, and imagine that
49//! > `polkadot-sdk` somehow magically solved the determinism in blockchains, and now a blockchain
50//! > can make HTTP calls and it will all work. This is absolutely NOT the case. An HTTP call made
51//! > by the offchain worker is non-deterministic by design. Blockchains can't and always won't be
52//! > able to perform non-deterministic operations such as making HTTP calls to a foreign server.
53//!
54//! ## FRAME's API
55//!
56//! [`frame`] provides a simple API through which pallets can define offchain worker functions. This
57//! is part of [`frame::traits::Hooks`], which is implemented as a part of
58//! [`frame::pallet_macros::hooks`].
59//!
60//! ```
61//! #[frame::pallet]
62//! pub mod pallet {
63//! use frame::prelude::*;
64//!
65//! #[pallet::config]
66//! pub trait Config: frame_system::Config {}
67//!
68//! #[pallet::pallet]
69//! pub struct Pallet<T>(_);
70//!
71//! #[pallet::hooks]
72//! impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
73//! fn offchain_worker(block_number: BlockNumberFor<T>) {
74//! // ...
75//! }
76//! }
77//! }
78//! ```
79//!
80//! Additionally, [`sp_runtime::offchain`] provides a set of utilities that can be used to moderate
81//! the execution of offchain workers.
82//!
83//! ## Think Twice: Why Use Substrate's Offchain Workers?
84//!
85//! Consider the fact that in principle, an offchain worker code written using the above API is no
86//! different than an equivalent written with an _actual offchain interaction library_, such as
87//! [Polkadot-JS](https://polkadot.js.org/docs/), or any of the other ones listed [here](https://github.com/substrate-developer-hub/awesome-substrate?tab=readme-ov-file#client-libraries).
88//!
89//! They can both read from the state, and have no means of updating the state, other than the route
90//! of submitting an extrinsic to the chain. Therefore, it is worth thinking twice before embedding
91//! a logic as a part of Substrate's offchain worker API. Does it have to be there? Can it not be a
92//! simple, actual offchain application that lives outside of the chain's WASM blob?
93//!
94//! Some of the reasons why you might want to do the opposite, and actually embed an offchain worker
95//! API into the WASM blob are:
96//!
97//! * Accessing the state is easier within the `offchain_worker` function, as it is already a part
98//! of the runtime, and [`frame::pallet_macros::storage`] provides all the tools needed to read
99//! the state. Other client libraries might provide varying degrees of capability here.
100//! * It will be updated in synchrony with the runtime. A Substrate's offchain application is part
101//! of the same WASM blob, and is therefore guaranteed to be up to date.
102//!
103//! For example, imagine you have modified a storage item to have a new type. This will possibly
104//! require a [`crate::reference_docs::frame_runtime_upgrades_and_migrations`], and any offchain
105//! code, such as a Polkadot-JS application, will have to be updated to reflect this change. Whereas
106//! the WASM offchain worker code is guaranteed to already be updated, or else the runtime code will
107//! not even compile.
108//!
109//!
110//! ## Further References
111//!
112//! - <https://forum.polkadot.network/t/offchain-workers-design-assumptions-vulnerabilities/2548>
113//! - <https://substrate.stackexchange.com/questions/11058/how-can-i-create-ocw-that-wont-activates-every-block-but-will-activates-only-w/11060#11060>
114//! - [Offchain worker example](https://github.com/paritytech/polkadot-sdk/tree/master/substrate/frame/examples/offchain-worker)