polkadot_sdk_docs/guides/handling_parachain_forks.rs
1//! # Parachain forks
2//!
3//! In this guide, we will examine how AURA-based parachains handle forks. AURA (Authority Round) is
4//! a consensus mechanism where block authors rotate at fixed time intervals. Each author gets a
5//! predetermined time slice during which they are allowed to author a block. On its own, this
6//! mechanism is fork-free.
7//!
8//! However, since the relay chain provides security and serves as the source of truth for
9//! parachains, the parachain is dependent on it. This relationship can introduce complexities that
10//! lead to forking scenarios.
11//!
12//! ## Background
13//! Each parachain block has a relay parent, which is a relay chain block that provides context to
14//! our parachain block. The constraints the relay chain imposes on our parachain can cause forks
15//! under certain conditions. With asynchronous-backing enabled chains, the node side is building
16//! blocks on all relay chain forks. This means that no matter which fork of the relay chain
17//! ultimately progressed, the parachain would have a block ready for that fork. The situation
18//! changes when parachains want to produce blocks at a faster cadence. In a scenario where a
19//! parachain might author on 3 cores with elastic scaling, it is not possible to author on all
20//! relay chain forks. The time constraints do not allow it. Building on two forks would result in 6
21//! blocks. The authoring of these blocks would consume more time than we have available before the
22//! next relay chain block arrives. This limitation requires a more fork-resistant approach to
23//! block-building.
24//!
25//! ## Impact of Forks
26//! When a relay chain fork occurs and the parachain builds on a fork that will not be extended in
27//! the future, the blocks built on that fork are lost and need to be rebuilt. This increases
28//! latency and reduces throughput, affecting the overall performance of the parachain.
29//!
30//! # Building on Older Pelay Parents
31//! Cumulus offers a way to mitigate the occurence of forks. Instead of picking a block at the tip
32//! of the relay chain to build blocks, the node side can pick a relay chain block that is older. By
33//! building on 12s old relay chain blocks, forks will already have settled and the parachain can
34//! build fork-free.
35//!
36//! ```text
37//! Without offset:
38//! Relay Chain: A --- B --- C --- D --- E
39//! \
40//! --- D' --- E'
41//! Parachain: X --- Y --- ? (builds on both D and D', wasting resources)
42//!
43//! With offset (2 blocks):
44//! Relay Chain: A --- B --- C --- D --- E
45//! \
46//! --- D' --- E'
47//! Parachain: X(A) - Y (B) - Z (on C, fork already resolved)
48//! ```
49//! **Note:** It is possible that relay chain forks extend over more than 1-2 blocks. However, it is
50//! unlikely.
51//! ## Tradeoffs
52//! Fork-free parachains come with a few tradeoffs:
53//! - The latency of incoming XCM messages will be delayed by `N * 6s`, where `N` is the number of
54//! relay chain blocks we want to offset by. For example, by building 2 relay chain blocks behind
55//! the tip, the XCM latency will be increased by 12 seconds.
56//! - The available PoV space will be slightly reduced. Assuming a 10mb PoV, parachains need to be
57//! ready to sacrifice around 0.5% of PoV space.
58//!
59//! ## Enabling Guide
60//! The decision whether the parachain should build on older relay parents is embedded into the
61//! runtime. After the changes are implemented, the runtime will enforce that no author can build
62//! with an offset smaller than the desired offset. If you wish to keep your current parachain
63//! behaviour and do not want aforementioned tradeoffs, set the offset to 0.
64//!
65//! **Note:** The APIs mentioned here are available in `polkadot-sdk` versions after `stable-2506`.
66//!
67//! 1. Define the relay parent offset your parachain should respect in the runtime.
68//! ```ignore
69//! const RELAY_PARENT_OFFSET = 2;
70//! ```
71//! 2. Pass this constant to the `parachain-system` pallet.
72//!
73//! ```ignore
74//! impl cumulus_pallet_parachain_system::Config for Runtime {
75//! // Other config items here
76//! ...
77//! type SelectCore = DefaultCoreSelector<Test>;
78//! type RelayParentOffset = ConstU32<RELAY_PARENT_OFFSET>;
79//! }
80//! ```
81//! 3. Implement the `RelayParentOffsetApi` runtime API for your runtime.
82//!
83//! ```ignore
84//! impl cumulus_primitives_core::RelayParentOffsetApi<Block> for Runtime {
85//! fn relay_parent_offset() -> u32 {
86//! RELAY_PARENT_OFFSET
87//! }
88//! }
89//! ```
90//! 4. Increase the `UNINCLUDED_SEGMENT_CAPICITY` for your runtime. It needs to be increased by
91//! `RELAY_PARENT_OFFSET * BLOCK_PROCESSING_VELOCITY`.