Kian Paimani
2024-02-28 13:07:14 +00:00
committed by GitHub
parent f4eedcebaf
commit 14530269b7
5 changed files with 126 additions and 2 deletions
Generated
+2
View File
@@ -13416,6 +13416,7 @@ dependencies = [
"pallet-collective",
"pallet-default-config-example",
"pallet-democracy",
"pallet-example-offchain-worker",
"pallet-example-single-block-migrations",
"pallet-examples",
"pallet-multisig",
@@ -13442,6 +13443,7 @@ dependencies = [
"sp-core",
"sp-io",
"sp-keyring",
"sp-offchain",
"sp-runtime",
"sp-version",
"staging-chain-spec-builder",
+4 -1
View File
@@ -23,6 +23,7 @@ frame = { path = "../../substrate/frame", features = [
] }
pallet-examples = { path = "../../substrate/frame/examples" }
pallet-default-config-example = { path = "../../substrate/frame/examples/default-config" }
pallet-example-offchain-worker = { path = "../../substrate/frame/examples/offchain-worker" }
# How we build docs in rust-docs
simple-mermaid = "0.1.1"
@@ -38,7 +39,7 @@ frame-support = { path = "../../substrate/frame/support", default-features = fal
frame-executive = { path = "../../substrate/frame/executive", default-features = false }
pallet-example-single-block-migrations = { path = "../../substrate/frame/examples/single-block-migrations" }
# Substrate
# Substrate Client
sc-network = { path = "../../substrate/client/network" }
sc-rpc-api = { path = "../../substrate/client/rpc-api" }
sc-rpc = { path = "../../substrate/client/rpc" }
@@ -50,6 +51,7 @@ sc-consensus-grandpa = { path = "../../substrate/client/consensus/grandpa" }
sc-consensus-beefy = { path = "../../substrate/client/consensus/beefy" }
sc-consensus-manual-seal = { path = "../../substrate/client/consensus/manual-seal" }
sc-consensus-pow = { path = "../../substrate/client/consensus/pow" }
substrate-wasm-builder = { path = "../../substrate/utils/wasm-builder" }
# Cumulus
@@ -78,6 +80,7 @@ sp-api = { path = "../../substrate/primitives/api" }
sp-core = { path = "../../substrate/primitives/core" }
sp-keyring = { path = "../../substrate/primitives/keyring" }
sp-runtime = { path = "../../substrate/primitives/runtime" }
sp-offchain = { path = "../../substrate/primitives/offchain" }
sp-version = { path = "../../substrate/primitives/version" }
# XCM
@@ -0,0 +1,115 @@
//! # 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:
//!
//! 1. 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.
//! 2. 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](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).
//!
//! 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, and [`frame::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.
//!
//!
//! ## Further References
//!
//! - <https://forum.polkadot.network/t/offchain-workers-design-assumptions-vulnerabilities/2548>
//! - <https://substrate.stackexchange.com/questions/11058/how-can-i-create-ocw-that-wont-activates-every-block-but-will-activates-only-w/11060#11060>
//! - [Offchain worker example](https://github.com/paritytech/polkadot-sdk/tree/master/substrate/frame/examples/offchain-worker)
+4
View File
@@ -99,3 +99,7 @@ pub mod frame_runtime_upgrades_and_migrations;
/// light-node-first out of the box.
// TODO: @jsdw @josepot https://github.com/paritytech/polkadot-sdk-docs/issues/68
pub mod light_nodes;
/// Learn about the offchain workers, how they function, and how to use them, as provided by the
/// [`frame`] APIs.
pub mod frame_offchain_workers;
+1 -1
View File
@@ -446,7 +446,7 @@ pub trait Hooks<BlockNumber> {
}
/// 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.
/// dispatched as separate threads, and entirely independent of the main blockchain execution.
///
/// 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