polkadot_sdk_docs/guides/async_backing_guide.rs
1//! # Upgrade Parachain for Asynchronous Backing Compatibility
2//!
3//! This guide is relevant for cumulus based parachain projects started in 2023 or before, whose
4//! backing process is synchronous where parablocks can only be built on the latest Relay Chain
5//! block. Async Backing allows collators to build parablocks on older Relay Chain blocks and create
6//! pipelines of multiple pending parablocks. This parallel block generation increases efficiency
7//! and throughput. For more information on Async backing and its terminology, refer to the document
8//! on [the Polkadot SDK docs.](https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/guides/async_backing_guide/index.html)
9//!
10//! > If starting a new parachain project, please use an async backing compatible template such as
11//! > the
12//! > [parachain template](https://github.com/paritytech/polkadot-sdk/tree/master/templates/parachain).
13//! The rollout process for Async Backing has three phases. Phases 1 and 2 below put new
14//! infrastructure in place. Then we can simply turn on async backing in phase 3.
15//!
16//! ## Prerequisite
17//!
18//! The relay chain needs to have async backing enabled so double-check that the relay-chain
19//! configuration contains the following parameter (especially when testing locally e.g. with
20//! zombienet):
21//!
22//! ```json
23//! "scheduler_params": {
24//! "lookahead": 3
25//! }
26//! ```
27//!
28//! <div class="warning"><code>lookahead</code> must be set to at least 3, otherwise parachain
29//! block times will degrade to worse than with sync backing!</div>
30//!
31//! Note: `async_backing_params` (`max_candidate_depth` and `allowed_ancestry_len`) are no longer
32//! required to be explicitly configured. Async backing works with them set to their default values
33//! of 0.
34//!
35//! ## Phase 1 - Update Parachain Runtime
36//!
37//! This phase involves configuring your parachain’s runtime `/runtime/src/lib.rs` to make use of
38//! async backing system.
39//!
40//! 1. Establish and ensure constants for `capacity` and `velocity` in the runtime.
41//! 2. Establish and ensure the constant relay chain slot duration measured in milliseconds equal to
42//! `6000` in the runtime.
43//! ```rust
44//! // Maximum number of blocks simultaneously accepted by the Runtime, not yet included into the
45//! // relay chain.
46//! pub const UNINCLUDED_SEGMENT_CAPACITY: u32 = 3;
47//! // How many parachain blocks are processed by the relay chain per parent. Limits the number of
48//! // blocks authored per slot.
49//! pub const BLOCK_PROCESSING_VELOCITY: u32 = 1;
50//! // Relay chain slot duration, in milliseconds.
51//! pub const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32 = 6000;
52//! ```
53//!
54//! 3. Establish constants `MILLISECS_PER_BLOCK` and `SLOT_DURATION` if not already present in the
55//! runtime.
56//! ```ignore
57//! // `SLOT_DURATION` is picked up by `pallet_timestamp` which is in turn picked
58//! // up by `pallet_aura` to implement `fn slot_duration()`.
59//! //
60//! // Change this to adjust the block time.
61//! pub const MILLISECS_PER_BLOCK: u64 = 12000;
62//! pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK;
63//! ```
64//!
65//! 4. Configure `cumulus_pallet_parachain_system` in the runtime.
66//!
67//! - Define a `FixedVelocityConsensusHook` using our capacity, velocity, and relay slot duration
68//! constants. Use this to set the parachain system `ConsensusHook` property.
69#![doc = docify::embed!("../../templates/parachain/runtime/src/lib.rs", ConsensusHook)]
70//! ```ignore
71//! impl cumulus_pallet_parachain_system::Config for Runtime {
72//! ..
73//! type ConsensusHook = ConsensusHook;
74//! ..
75//! }
76//! ```
77//! - Set the parachain system property `CheckAssociatedRelayNumber` to
78//! `RelayNumberMonotonicallyIncreases`
79//! ```ignore
80//! impl cumulus_pallet_parachain_system::Config for Runtime {
81//! ..
82//! type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases;
83//! ..
84//! }
85//! ```
86//!
87//! 5. Configure `pallet_aura` in the runtime.
88//!
89//! - Set `AllowMultipleBlocksPerSlot` to `true` to allow multiple blocks per slot.
90//!
91//! - Define `pallet_aura::SlotDuration` using our constant `SLOT_DURATION`
92//! ```ignore
93//! impl pallet_aura::Config for Runtime {
94//! ..
95//! type AllowMultipleBlocksPerSlot = ConstBool<true>;
96//! type SlotDuration = ConstU64<SLOT_DURATION>;
97//! ..
98//! }
99//! ```
100//!
101//! 6. Update `sp_consensus_aura::AuraApi::slot_duration` in `sp_api::impl_runtime_apis` to match
102//! the constant `SLOT_DURATION`
103#![doc = docify::embed!("../../templates/parachain/runtime/src/apis.rs", impl_slot_duration)]
104//! 7. Implement the `AuraUnincludedSegmentApi`, which allows the collator client to query its
105//! runtime to determine whether it should author a block.
106//!
107//! - Add the dependency `cumulus-primitives-aura` to the `runtime/Cargo.toml` file for your
108//! runtime
109//! ```ignore
110//! ..
111//! cumulus-primitives-aura = { path = "../../../../primitives/aura", default-features = false }
112//! ..
113//! ```
114//!
115//! - In the same file, add `"cumulus-primitives-aura/std",` to the `std` feature.
116//!
117//! - Inside the `impl_runtime_apis!` block for your runtime, implement the
118//! `cumulus_primitives_aura::AuraUnincludedSegmentApi` as shown below.
119#![doc = docify::embed!("../../templates/parachain/runtime/src/apis.rs", impl_can_build_upon)]
120//! **Note:** With the default capacity of 3 and velocity of 1, a single parachain block is
121//! authored per relay chain block, giving a 6 second parachain block time.
122//!
123//! 8. If your `runtime/src/lib.rs` provides a `CheckInherents` type to `register_validate_block`,
124//! remove it. `FixedVelocityConsensusHook` makes it unnecessary. The following example shows how
125//! `register_validate_block` should look after removing `CheckInherents`.
126#![doc = docify::embed!("../../templates/parachain/runtime/src/lib.rs", register_validate_block)]
127//! ## Phase 2 - Update Parachain Nodes
128//!
129//! This phase consists of plugging in the new lookahead collator node.
130//!
131//! 1. Import `cumulus_primitives_core::ValidationCode` to `node/src/service.rs`.
132#![doc = docify::embed!("../../templates/parachain/node/src/service.rs", cumulus_primitives)]
133//! 2. In `node/src/service.rs`, modify `sc_service::spawn_tasks` to use a clone of `Backend` rather
134//! than the original
135//! ```ignore
136//! sc_service::spawn_tasks(sc_service::SpawnTasksParams {
137//! ..
138//! backend: backend.clone(),
139//! ..
140//! })?;
141//! ```
142//!
143//! 3. Add `backend` as a parameter to `start_consensus()` in `node/src/service.rs`
144//! ```text
145//! fn start_consensus(
146//! ..
147//! backend: Arc<ParachainBackend>,
148//! ..
149//! ```
150//! ```ignore
151//! if validator {
152//! start_consensus(
153//! ..
154//! backend.clone(),
155//! ..
156//! )?;
157//! }
158//! ```
159//!
160//! 4. In `node/src/service.rs` import the lookahead collator rather than the basic collator
161#![doc = docify::embed!("../../templates/parachain/node/src/service.rs", lookahead_collator)]
162//! 5. In `start_consensus()` replace the `BasicAuraParams` struct with `AuraParams`
163//! - Change the struct type from `BasicAuraParams` to `AuraParams`
164//! - In the `para_client` field, pass in a cloned para client rather than the original
165//! - Add a `para_backend` parameter after `para_client`, passing in our para backend
166//! - Provide a `code_hash_provider` closure like that shown below
167//! - Increase `authoring_duration` from 500 milliseconds to 2000
168//! ```ignore
169//! let params = AuraParams {
170//! ..
171//! para_client: client.clone(),
172//! para_backend: backend.clone(),
173//! ..
174//! code_hash_provider: move |block_hash| {
175//! client.code_at(block_hash).ok().map(|c| ValidationCode::from(c).hash())
176//! },
177//! ..
178//! authoring_duration: Duration::from_millis(2000),
179//! ..
180//! };
181//! ```
182//!
183//! **Note:** Set `authoring_duration` to whatever you want, taking your own hardware into account.
184//! But if the backer who should be slower than you due to reading from disk, times out at two
185//! seconds your candidates will be rejected.
186//!
187//! 6. In `start_consensus()` replace `basic_aura::run` with `aura::run`
188//! ```ignore
189//! let fut =
190//! aura::run::<Block, sp_consensus_aura::sr25519::AuthorityPair, _, _, _, _, _, _, _, _, _>(
191//! params,
192//! );
193//! task_manager.spawn_essential_handle().spawn("aura", None, fut);
194//! ```
195//!
196//! ## Phase 3 - Activate Async Backing
197//!
198//! This phase consists of changes to your parachain’s runtime that activate async backing feature.
199//!
200//! 1. Verify `pallet_aura` has `AllowMultipleBlocksPerSlot` set to `true` in `runtime/src/lib.rs`
201//! (this should already be done from Phase 1).
202#![doc = docify::embed!("../../templates/parachain/runtime/src/configs/mod.rs", aura_config)]
203//! 2. Verify `UNINCLUDED_SEGMENT_CAPACITY` is set to at least `3` in `runtime/src/lib.rs`.
204#![doc = docify::embed!("../../templates/parachain/runtime/src/lib.rs", async_backing_params)]
205//! 3. Decrease `MILLISECS_PER_BLOCK` to 6000.
206//!
207//! - Note: For a parachain which measures time in terms of its own block number rather than by
208//! relay block number it may be preferable to increase velocity. Changing block time may cause
209//! complications, requiring additional changes. See the section “Timing by Block Number”.
210#![doc = docify::embed!("../../templates/parachain/runtime/src/lib.rs", block_times)]
211//! 4. Update `MAXIMUM_BLOCK_WEIGHT` to reflect the increased time available for block production.
212#![doc = docify::embed!("../../templates/parachain/runtime/src/lib.rs", max_block_weight)]
213//! 5. Set `MinimumPeriod` to `0` in `pallet_timestamp`. This is required to allow multiple blocks
214//! within the same slot.
215//! ```ignore
216//! impl pallet_timestamp::Config for Runtime {
217//! ..
218//! type MinimumPeriod = ConstU64<0>;
219//! ..
220//! }
221//! ```
222//!
223//! ## Timing by Block Number
224//!
225//! With asynchronous backing, parachains produce blocks every 6 seconds rather than 12 seconds.
226//! This means that any on-chain logic that derives time from parachain block numbers will see
227//! time move twice as fast. This could result in expected and actual time not matching up,
228//! potentially causing issues with vesting schedules, unlock periods, or other time-dependent
229//! logic.
230//!
231//! The recommended strategy is to rely on relay chain block numbers for timing instead of
232//! parachain block numbers. Relay block number is kept track of by each parachain in
233//! `pallet-parachain-system` with the storage value `LastRelayChainBlockNumber`. This value can
234//! be obtained and used wherever timing based on block number is needed.
235//!
236//! Alternatively, `pallet_timestamp` provides wall-clock time which is independent of block
237//! number and is not affected by changes in block time.
238
239#![deny(rustdoc::broken_intra_doc_links)]
240#![deny(rustdoc::private_intra_doc_links)]