Skip to content

Commit 4bd3ac7

Browse files
DrW3RKkianenigma
authored andcommitted
Add Async Backing guide (paritytech#4363)
Update the instructions to work with the latest parachain template on Polkadot SDK --------- Co-authored-by: kianenigma <[email protected]> Co-authored-by: Kian Paimani <[email protected]>
1 parent 974610c commit 4bd3ac7

6 files changed

Lines changed: 308 additions & 21 deletions

File tree

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
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 Wiki.](https://wiki.polkadot.network/docs/maintain-guides-async-backing)
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 three parameters (especially when testing locally e.g. with
20+
//! zombienet):
21+
//!
22+
//! ```json
23+
//! "async_backing_params": {
24+
//! "max_candidate_depth": 3,
25+
//! "allowed_ancestry_len": 2
26+
//! },
27+
//! "scheduling_lookahead": 2
28+
//! ```
29+
//!
30+
//! <div class="warning">`scheduling_lookahead` must be set to 2, otherwise parachain block times
31+
//! will degrade to worse than with sync backing!</div>
32+
//!
33+
//! ## Phase 1 - Update Parachain Runtime
34+
//!
35+
//! This phase involves configuring your parachain’s runtime `/runtime/src/lib.rs` to make use of
36+
//! async backing system.
37+
//!
38+
//! 1. Establish and ensure constants for `capacity` and `velocity` are both set to 1 in the
39+
//! runtime.
40+
//! 2. Establish and ensure the constant relay chain slot duration measured in milliseconds equal to
41+
//! `6000` in the runtime.
42+
//! ```rust
43+
//! // Maximum number of blocks simultaneously accepted by the Runtime, not yet included into the
44+
//! // relay chain.
45+
//! pub const UNINCLUDED_SEGMENT_CAPACITY: u32 = 1;
46+
//! // How many parachain blocks are processed by the relay chain per parent. Limits the number of
47+
//! // blocks authored per slot.
48+
//! pub const BLOCK_PROCESSING_VELOCITY: u32 = 1;
49+
//! // Relay chain slot duration, in milliseconds.
50+
//! pub const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32 = 6000;
51+
//! ```
52+
//!
53+
//! 3. Establish constants `MILLISECS_PER_BLOCK` and `SLOT_DURATION` if not already present in the
54+
//! runtime.
55+
//! ```ignore
56+
//! // `SLOT_DURATION` is picked up by `pallet_timestamp` which is in turn picked
57+
//! // up by `pallet_aura` to implement `fn slot_duration()`.
58+
//! //
59+
//! // Change this to adjust the block time.
60+
//! pub const MILLISECS_PER_BLOCK: u64 = 12000;
61+
//! pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK;
62+
//! ```
63+
//!
64+
//! 4. Configure `cumulus_pallet_parachain_system` in the runtime.
65+
//!
66+
//! - Define a `FixedVelocityConsensusHook` using our capacity, velocity, and relay slot duration
67+
//! constants. Use this to set the parachain system `ConsensusHook` property.
68+
#![doc = docify::embed!("../../templates/parachain/runtime/src/lib.rs", ConsensusHook)]
69+
//! ```ignore
70+
//! impl cumulus_pallet_parachain_system::Config for Runtime {
71+
//! ..
72+
//! type ConsensusHook = ConsensusHook;
73+
//! ..
74+
//! }
75+
//! ```
76+
//! - Set the parachain system property `CheckAssociatedRelayNumber` to
77+
//! `RelayNumberMonotonicallyIncreases`
78+
//! ```ignore
79+
//! impl cumulus_pallet_parachain_system::Config for Runtime {
80+
//! ..
81+
//! type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases;
82+
//! ..
83+
//! }
84+
//! ```
85+
//!
86+
//! 5. Configure `pallet_aura` in the runtime.
87+
//!
88+
//! - Set `AllowMultipleBlocksPerSlot` to `false` (don't worry, we will set it to `true` when we
89+
//! activate async backing in phase 3).
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<false>;
96+
//! #[cfg(feature = "experimental")]
97+
//! type SlotDuration = ConstU64<SLOT_DURATION>;
98+
//! ..
99+
//! }
100+
//! ```
101+
//!
102+
//! 6. Update `sp_consensus_aura::AuraApi::slot_duration` in `sp_api::impl_runtime_apis` to match
103+
//! the constant `SLOT_DURATION`
104+
#![doc = docify::embed!("../../templates/parachain/runtime/src/apis.rs", impl_slot_duration)]
105+
//!
106+
//! 7. Implement the `AuraUnincludedSegmentApi`, which allows the collator client to query its
107+
//! runtime to determine whether it should author a block.
108+
//!
109+
//! - Add the dependency `cumulus-primitives-aura` to the `runtime/Cargo.toml` file for your
110+
//! runtime
111+
//! ```ignore
112+
//! ..
113+
//! cumulus-primitives-aura = { path = "../../../../primitives/aura", default-features = false }
114+
//! ..
115+
//! ```
116+
//!
117+
//! - In the same file, add `"cumulus-primitives-aura/std",` to the `std` feature.
118+
//!
119+
//! - Inside the `impl_runtime_apis!` block for your runtime, implement the
120+
//! `cumulus_primitives_aura::AuraUnincludedSegmentApi` as shown below.
121+
#![doc = docify::embed!("../../templates/parachain/runtime/src/apis.rs", impl_can_build_upon)]
122+
//!
123+
//! **Note:** With a capacity of 1 we have an effective velocity of ½ even when velocity is
124+
//! configured to some larger value. This is because capacity will be filled after a single block is
125+
//! produced and will only be freed up after that block is included on the relay chain, which takes
126+
//! 2 relay blocks to accomplish. Thus with capacity 1 and velocity 1 we get the customary 12 second
127+
//! parachain block time.
128+
//!
129+
//! 8. If your `runtime/src/lib.rs` provides a `CheckInherents` type to `register_validate_block`,
130+
//! remove it. `FixedVelocityConsensusHook` makes it unnecessary. The following example shows how
131+
//! `register_validate_block` should look after removing `CheckInherents`.
132+
#![doc = docify::embed!("../../templates/parachain/runtime/src/lib.rs", register_validate_block)]
133+
//!
134+
//!
135+
//! ## Phase 2 - Update Parachain Nodes
136+
//!
137+
//! This phase consists of plugging in the new lookahead collator node.
138+
//!
139+
//! 1. Import `cumulus_primitives_core::ValidationCode` to `node/src/service.rs`.
140+
#![doc = docify::embed!("../../templates/parachain/node/src/service.rs", cumulus_primitives)]
141+
//!
142+
//! 2. In `node/src/service.rs`, modify `sc_service::spawn_tasks` to use a clone of `Backend` rather
143+
//! than the original
144+
//! ```ignore
145+
//! sc_service::spawn_tasks(sc_service::SpawnTasksParams {
146+
//! ..
147+
//! backend: backend.clone(),
148+
//! ..
149+
//! })?;
150+
//! ```
151+
//!
152+
//! 3. Add `backend` as a parameter to `start_consensus()` in `node/src/service.rs`
153+
//! ```text
154+
//! fn start_consensus(
155+
//! ..
156+
//! backend: Arc<ParachainBackend>,
157+
//! ..
158+
//! ```
159+
//! ```ignore
160+
//! if validator {
161+
//! start_consensus(
162+
//! ..
163+
//! backend.clone(),
164+
//! ..
165+
//! )?;
166+
//! }
167+
//! ```
168+
//!
169+
//! 4. In `node/src/service.rs` import the lookahead collator rather than the basic collator
170+
#![doc = docify::embed!("../../templates/parachain/node/src/service.rs", lookahead_collator)]
171+
//!
172+
//! 5. In `start_consensus()` replace the `BasicAuraParams` struct with `AuraParams`
173+
//! - Change the struct type from `BasicAuraParams` to `AuraParams`
174+
//! - In the `para_client` field, pass in a cloned para client rather than the original
175+
//! - Add a `para_backend` parameter after `para_client`, passing in our para backend
176+
//! - Provide a `code_hash_provider` closure like that shown below
177+
//! - Increase `authoring_duration` from 500 milliseconds to 1500
178+
//! ```ignore
179+
//! let params = AuraParams {
180+
//! ..
181+
//! para_client: client.clone(),
182+
//! para_backend: backend.clone(),
183+
//! ..
184+
//! code_hash_provider: move |block_hash| {
185+
//! client.code_at(block_hash).ok().map(|c| ValidationCode::from(c).hash())
186+
//! },
187+
//! ..
188+
//! authoring_duration: Duration::from_millis(1500),
189+
//! ..
190+
//! };
191+
//! ```
192+
//!
193+
//! **Note:** Set `authoring_duration` to whatever you want, taking your own hardware into account.
194+
//! But if the backer who should be slower than you due to reading from disk, times out at two
195+
//! seconds your candidates will be rejected.
196+
//!
197+
//! 6. In `start_consensus()` replace `basic_aura::run` with `aura::run`
198+
//! ```ignore
199+
//! let fut =
200+
//! aura::run::<Block, sp_consensus_aura::sr25519::AuthorityPair, _, _, _, _, _, _, _, _, _>(
201+
//! params,
202+
//! );
203+
//! task_manager.spawn_essential_handle().spawn("aura", None, fut);
204+
//! ```
205+
//!
206+
//! ## Phase 3 - Activate Async Backing
207+
//!
208+
//! This phase consists of changes to your parachain’s runtime that activate async backing feature.
209+
//!
210+
//! 1. Configure `pallet_aura`, setting `AllowMultipleBlocksPerSlot` to true in
211+
//! `runtime/src/lib.rs`.
212+
#![doc = docify::embed!("../../templates/parachain/runtime/src/configs/mod.rs", aura_config)]
213+
//!
214+
//! 2. Increase the maximum `UNINCLUDED_SEGMENT_CAPACITY` in `runtime/src/lib.rs`.
215+
#![doc = docify::embed!("../../templates/parachain/runtime/src/lib.rs", async_backing_params)]
216+
//!
217+
//! 3. Decrease `MILLISECS_PER_BLOCK` to 6000.
218+
//!
219+
//! - Note: For a parachain which measures time in terms of its own block number rather than by
220+
//! relay block number it may be preferable to increase velocity. Changing block time may cause
221+
//! complications, requiring additional changes. See the section “Timing by Block Number”.
222+
#![doc = docify::embed!("../../templates/parachain/runtime/src/lib.rs", block_times)]
223+
//!
224+
//! 4. Update `MAXIMUM_BLOCK_WEIGHT` to reflect the increased time available for block production.
225+
#![doc = docify::embed!("../../templates/parachain/runtime/src/lib.rs", max_block_weight)]
226+
//!
227+
//! 5. Add a feature flagged alternative for `MinimumPeriod` in `pallet_timestamp`. The type should
228+
//! be `ConstU64<0>` with the feature flag experimental, and `ConstU64<{SLOT_DURATION / 2}>`
229+
//! without.
230+
//! ```ignore
231+
//! impl pallet_timestamp::Config for Runtime {
232+
//! ..
233+
//! #[cfg(feature = "experimental")]
234+
//! type MinimumPeriod = ConstU64<0>;
235+
//! #[cfg(not(feature = "experimental"))]
236+
//! type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>;
237+
//! ..
238+
//! }
239+
//! ```
240+
//!
241+
//! ## Timing by Block Number
242+
//!
243+
//! With asynchronous backing it will be possible for parachains to opt for a block time of 6
244+
//! seconds rather than 12 seconds. But modifying block duration isn’t so simple for a parachain
245+
//! which was measuring time in terms of its own block number. It could result in expected and
246+
//! actual time not matching up, stalling the parachain.
247+
//!
248+
//! One strategy to deal with this issue is to instead rely on relay chain block numbers for timing.
249+
//! Relay block number is kept track of by each parachain in `pallet-parachain-system` with the
250+
//! storage value `LastRelayChainBlockNumber`. This value can be obtained and used wherever timing
251+
//! based on block number is needed.
252+
253+
#![deny(rustdoc::broken_intra_doc_links)]
254+
#![deny(rustdoc::private_intra_doc_links)]

docs/sdk/src/guides/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,8 @@ pub mod your_first_runtime;
3636
/// How to enable storage weight reclaiming in a parachain node and runtime.
3737
pub mod enable_pov_reclaim;
3838

39+
/// How to enable Async Backing on parachain projects that started in 2023 or before.
40+
pub mod async_backing_guide;
41+
3942
/// How to enable metadata hash verification in the runtime.
4043
pub mod enable_metadata_hash;

templates/parachain/node/src/service.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use parachain_template_runtime::{
1212

1313
// Cumulus Imports
1414
use cumulus_client_collator::service::CollatorService;
15+
#[docify::export(lookahead_collator)]
1516
use cumulus_client_consensus_aura::collators::lookahead::{self as aura, Params as AuraParams};
1617
use cumulus_client_consensus_common::ParachainBlockImport as TParachainBlockImport;
1718
use cumulus_client_consensus_proposer::Proposer;
@@ -20,6 +21,7 @@ use cumulus_client_service::{
2021
BuildNetworkParams, CollatorSybilResistance, DARecoveryProfile, ParachainHostFunctions,
2122
StartRelayChainTasksParams,
2223
};
24+
#[docify::export(cumulus_primitives)]
2325
use cumulus_primitives_core::{
2426
relay_chain::{CollatorPair, ValidationCode},
2527
ParaId,

templates/parachain/runtime/src/apis.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,26 @@ use super::{
4747
SLOT_DURATION, VERSION,
4848
};
4949

50+
// we move some impls outside so we can easily use them with `docify`.
51+
impl Runtime {
52+
#[docify::export]
53+
fn impl_slot_duration() -> sp_consensus_aura::SlotDuration {
54+
sp_consensus_aura::SlotDuration::from_millis(SLOT_DURATION)
55+
}
56+
57+
#[docify::export]
58+
fn impl_can_build_upon(
59+
included_hash: <Block as BlockT>::Hash,
60+
slot: cumulus_primitives_aura::Slot,
61+
) -> bool {
62+
ConsensusHook::can_build_upon(included_hash, slot)
63+
}
64+
}
65+
5066
impl_runtime_apis! {
5167
impl sp_consensus_aura::AuraApi<Block, AuraId> for Runtime {
5268
fn slot_duration() -> sp_consensus_aura::SlotDuration {
53-
sp_consensus_aura::SlotDuration::from_millis(SLOT_DURATION)
69+
Runtime::impl_slot_duration()
5470
}
5571

5672
fn authorities() -> Vec<AuraId> {
@@ -63,7 +79,7 @@ impl_runtime_apis! {
6379
included_hash: <Block as BlockT>::Hash,
6480
slot: cumulus_primitives_aura::Slot,
6581
) -> bool {
66-
ConsensusHook::can_build_upon(included_hash, slot)
82+
Runtime::impl_can_build_upon(included_hash, slot)
6783
}
6884
}
6985

templates/parachain/runtime/src/configs/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,7 @@ impl pallet_session::Config for Runtime {
265265
type WeightInfo = ();
266266
}
267267

268+
#[docify::export(aura_config)]
268269
impl pallet_aura::Config for Runtime {
269270
type AuthorityId = AuraId;
270271
type DisabledValidators = ();

0 commit comments

Comments
 (0)