diff --git a/docs/sdk/src/guides/async_backing_guide.rs b/docs/sdk/src/guides/async_backing_guide.rs new file mode 100644 index 000000000000..f2f4dcabfd29 --- /dev/null +++ b/docs/sdk/src/guides/async_backing_guide.rs @@ -0,0 +1,254 @@ +//! # Upgrade Parachain for Asynchronous Backing Compatibility +//! +//! This guide is relevant for cumulus based parachain projects started in 2023 or before, whose +//! backing process is synchronous where parablocks can only be built on the latest Relay Chain +//! block. Async Backing allows collators to build parablocks on older Relay Chain blocks and create +//! pipelines of multiple pending parablocks. This parallel block generation increases efficiency +//! and throughput. For more information on Async backing and its terminology, refer to the document +//! on [the Polkadot Wiki.](https://wiki.polkadot.network/docs/maintain-guides-async-backing) +//! +//! > If starting a new parachain project, please use an async backing compatible template such as +//! > the +//! > [parachain template](https://github.com/paritytech/polkadot-sdk/tree/master/templates/parachain). +//! The rollout process for Async Backing has three phases. Phases 1 and 2 below put new +//! infrastructure in place. Then we can simply turn on async backing in phase 3. +//! +//! ## Prerequisite +//! +//! The relay chain needs to have async backing enabled so double-check that the relay-chain +//! configuration contains the following three parameters (especially when testing locally e.g. with +//! zombienet): +//! +//! ```json +//! "async_backing_params": { +//! "max_candidate_depth": 3, +//! "allowed_ancestry_len": 2 +//! }, +//! "scheduling_lookahead": 2 +//! ``` +//! +//!
`scheduling_lookahead` must be set to 2, otherwise parachain block times +//! will degrade to worse than with sync backing!
+//! +//! ## Phase 1 - Update Parachain Runtime +//! +//! This phase involves configuring your parachain’s runtime `/runtime/src/lib.rs` to make use of +//! async backing system. +//! +//! 1. Establish and ensure constants for `capacity` and `velocity` are both set to 1 in the +//! runtime. +//! 2. Establish and ensure the constant relay chain slot duration measured in milliseconds equal to +//! `6000` in the runtime. +//! ```rust +//! // Maximum number of blocks simultaneously accepted by the Runtime, not yet included into the +//! // relay chain. +//! pub const UNINCLUDED_SEGMENT_CAPACITY: u32 = 1; +//! // How many parachain blocks are processed by the relay chain per parent. Limits the number of +//! // blocks authored per slot. +//! pub const BLOCK_PROCESSING_VELOCITY: u32 = 1; +//! // Relay chain slot duration, in milliseconds. +//! pub const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32 = 6000; +//! ``` +//! +//! 3. Establish constants `MILLISECS_PER_BLOCK` and `SLOT_DURATION` if not already present in the +//! runtime. +//! ```ignore +//! // `SLOT_DURATION` is picked up by `pallet_timestamp` which is in turn picked +//! // up by `pallet_aura` to implement `fn slot_duration()`. +//! // +//! // Change this to adjust the block time. +//! pub const MILLISECS_PER_BLOCK: u64 = 12000; +//! pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; +//! ``` +//! +//! 4. Configure `cumulus_pallet_parachain_system` in the runtime. +//! +//! - Define a `FixedVelocityConsensusHook` using our capacity, velocity, and relay slot duration +//! constants. Use this to set the parachain system `ConsensusHook` property. +#![doc = docify::embed!("../../templates/parachain/runtime/src/lib.rs", ConsensusHook)] +//! ```ignore +//! impl cumulus_pallet_parachain_system::Config for Runtime { +//! .. +//! type ConsensusHook = ConsensusHook; +//! .. +//! } +//! ``` +//! - Set the parachain system property `CheckAssociatedRelayNumber` to +//! `RelayNumberMonotonicallyIncreases` +//! ```ignore +//! impl cumulus_pallet_parachain_system::Config for Runtime { +//! .. +//! type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases; +//! .. +//! } +//! ``` +//! +//! 5. Configure `pallet_aura` in the runtime. +//! +//! - Set `AllowMultipleBlocksPerSlot` to `false` (don't worry, we will set it to `true` when we +//! activate async backing in phase 3). +//! +//! - Define `pallet_aura::SlotDuration` using our constant `SLOT_DURATION` +//! ```ignore +//! impl pallet_aura::Config for Runtime { +//! .. +//! type AllowMultipleBlocksPerSlot = ConstBool; +//! #[cfg(feature = "experimental")] +//! type SlotDuration = ConstU64; +//! .. +//! } +//! ``` +//! +//! 6. Update `sp_consensus_aura::AuraApi::slot_duration` in `sp_api::impl_runtime_apis` to match +//! the constant `SLOT_DURATION` +#![doc = docify::embed!("../../templates/parachain/runtime/src/apis.rs", impl_slot_duration)] +//! +//! 7. Implement the `AuraUnincludedSegmentApi`, which allows the collator client to query its +//! runtime to determine whether it should author a block. +//! +//! - Add the dependency `cumulus-primitives-aura` to the `runtime/Cargo.toml` file for your +//! runtime +//! ```ignore +//! .. +//! cumulus-primitives-aura = { path = "../../../../primitives/aura", default-features = false } +//! .. +//! ``` +//! +//! - In the same file, add `"cumulus-primitives-aura/std",` to the `std` feature. +//! +//! - Inside the `impl_runtime_apis!` block for your runtime, implement the +//! `cumulus_primitives_aura::AuraUnincludedSegmentApi` as shown below. +#![doc = docify::embed!("../../templates/parachain/runtime/src/apis.rs", impl_can_build_upon)] +//! +//! **Note:** With a capacity of 1 we have an effective velocity of ½ even when velocity is +//! configured to some larger value. This is because capacity will be filled after a single block is +//! produced and will only be freed up after that block is included on the relay chain, which takes +//! 2 relay blocks to accomplish. Thus with capacity 1 and velocity 1 we get the customary 12 second +//! parachain block time. +//! +//! 8. If your `runtime/src/lib.rs` provides a `CheckInherents` type to `register_validate_block`, +//! remove it. `FixedVelocityConsensusHook` makes it unnecessary. The following example shows how +//! `register_validate_block` should look after removing `CheckInherents`. +#![doc = docify::embed!("../../templates/parachain/runtime/src/lib.rs", register_validate_block)] +//! +//! +//! ## Phase 2 - Update Parachain Nodes +//! +//! This phase consists of plugging in the new lookahead collator node. +//! +//! 1. Import `cumulus_primitives_core::ValidationCode` to `node/src/service.rs`. +#![doc = docify::embed!("../../templates/parachain/node/src/service.rs", cumulus_primitives)] +//! +//! 2. In `node/src/service.rs`, modify `sc_service::spawn_tasks` to use a clone of `Backend` rather +//! than the original +//! ```ignore +//! sc_service::spawn_tasks(sc_service::SpawnTasksParams { +//! .. +//! backend: backend.clone(), +//! .. +//! })?; +//! ``` +//! +//! 3. Add `backend` as a parameter to `start_consensus()` in `node/src/service.rs` +//! ```text +//! fn start_consensus( +//! .. +//! backend: Arc, +//! .. +//! ``` +//! ```ignore +//! if validator { +//! start_consensus( +//! .. +//! backend.clone(), +//! .. +//! )?; +//! } +//! ``` +//! +//! 4. In `node/src/service.rs` import the lookahead collator rather than the basic collator +#![doc = docify::embed!("../../templates/parachain/node/src/service.rs", lookahead_collator)] +//! +//! 5. In `start_consensus()` replace the `BasicAuraParams` struct with `AuraParams` +//! - Change the struct type from `BasicAuraParams` to `AuraParams` +//! - In the `para_client` field, pass in a cloned para client rather than the original +//! - Add a `para_backend` parameter after `para_client`, passing in our para backend +//! - Provide a `code_hash_provider` closure like that shown below +//! - Increase `authoring_duration` from 500 milliseconds to 1500 +//! ```ignore +//! let params = AuraParams { +//! .. +//! para_client: client.clone(), +//! para_backend: backend.clone(), +//! .. +//! code_hash_provider: move |block_hash| { +//! client.code_at(block_hash).ok().map(|c| ValidationCode::from(c).hash()) +//! }, +//! .. +//! authoring_duration: Duration::from_millis(1500), +//! .. +//! }; +//! ``` +//! +//! **Note:** Set `authoring_duration` to whatever you want, taking your own hardware into account. +//! But if the backer who should be slower than you due to reading from disk, times out at two +//! seconds your candidates will be rejected. +//! +//! 6. In `start_consensus()` replace `basic_aura::run` with `aura::run` +//! ```ignore +//! let fut = +//! aura::run::( +//! params, +//! ); +//! task_manager.spawn_essential_handle().spawn("aura", None, fut); +//! ``` +//! +//! ## Phase 3 - Activate Async Backing +//! +//! This phase consists of changes to your parachain’s runtime that activate async backing feature. +//! +//! 1. Configure `pallet_aura`, setting `AllowMultipleBlocksPerSlot` to true in +//! `runtime/src/lib.rs`. +#![doc = docify::embed!("../../templates/parachain/runtime/src/configs/mod.rs", aura_config)] +//! +//! 2. Increase the maximum `UNINCLUDED_SEGMENT_CAPACITY` in `runtime/src/lib.rs`. +#![doc = docify::embed!("../../templates/parachain/runtime/src/lib.rs", async_backing_params)] +//! +//! 3. Decrease `MILLISECS_PER_BLOCK` to 6000. +//! +//! - Note: For a parachain which measures time in terms of its own block number rather than by +//! relay block number it may be preferable to increase velocity. Changing block time may cause +//! complications, requiring additional changes. See the section “Timing by Block Number”. +#![doc = docify::embed!("../../templates/parachain/runtime/src/lib.rs", block_times)] +//! +//! 4. Update `MAXIMUM_BLOCK_WEIGHT` to reflect the increased time available for block production. +#![doc = docify::embed!("../../templates/parachain/runtime/src/lib.rs", max_block_weight)] +//! +//! 5. Add a feature flagged alternative for `MinimumPeriod` in `pallet_timestamp`. The type should +//! be `ConstU64<0>` with the feature flag experimental, and `ConstU64<{SLOT_DURATION / 2}>` +//! without. +//! ```ignore +//! impl pallet_timestamp::Config for Runtime { +//! .. +//! #[cfg(feature = "experimental")] +//! type MinimumPeriod = ConstU64<0>; +//! #[cfg(not(feature = "experimental"))] +//! type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>; +//! .. +//! } +//! ``` +//! +//! ## Timing by Block Number +//! +//! With asynchronous backing it will be possible for parachains to opt for a block time of 6 +//! seconds rather than 12 seconds. But modifying block duration isn’t so simple for a parachain +//! which was measuring time in terms of its own block number. It could result in expected and +//! actual time not matching up, stalling the parachain. +//! +//! One strategy to deal with this issue is to instead rely on relay chain block numbers for timing. +//! Relay block number is kept track of by each parachain in `pallet-parachain-system` with the +//! storage value `LastRelayChainBlockNumber`. This value can be obtained and used wherever timing +//! based on block number is needed. + +#![deny(rustdoc::broken_intra_doc_links)] +#![deny(rustdoc::private_intra_doc_links)] diff --git a/docs/sdk/src/guides/mod.rs b/docs/sdk/src/guides/mod.rs index 485cdc30636f..8296ed447e14 100644 --- a/docs/sdk/src/guides/mod.rs +++ b/docs/sdk/src/guides/mod.rs @@ -36,5 +36,8 @@ pub mod your_first_runtime; /// How to enable storage weight reclaiming in a parachain node and runtime. pub mod enable_pov_reclaim; +/// How to enable Async Backing on parachain projects that started in 2023 or before. +pub mod async_backing_guide; + /// How to enable metadata hash verification in the runtime. pub mod enable_metadata_hash; diff --git a/templates/parachain/node/src/service.rs b/templates/parachain/node/src/service.rs index 587dd19faf3e..bf44207acc9c 100644 --- a/templates/parachain/node/src/service.rs +++ b/templates/parachain/node/src/service.rs @@ -12,6 +12,7 @@ use parachain_template_runtime::{ // Cumulus Imports use cumulus_client_collator::service::CollatorService; +#[docify::export(lookahead_collator)] use cumulus_client_consensus_aura::collators::lookahead::{self as aura, Params as AuraParams}; use cumulus_client_consensus_common::ParachainBlockImport as TParachainBlockImport; use cumulus_client_consensus_proposer::Proposer; @@ -20,6 +21,7 @@ use cumulus_client_service::{ BuildNetworkParams, CollatorSybilResistance, DARecoveryProfile, ParachainHostFunctions, StartRelayChainTasksParams, }; +#[docify::export(cumulus_primitives)] use cumulus_primitives_core::{ relay_chain::{CollatorPair, ValidationCode}, ParaId, diff --git a/templates/parachain/runtime/src/apis.rs b/templates/parachain/runtime/src/apis.rs index 107956ded410..bc3c3808fb78 100644 --- a/templates/parachain/runtime/src/apis.rs +++ b/templates/parachain/runtime/src/apis.rs @@ -47,10 +47,26 @@ use super::{ SLOT_DURATION, VERSION, }; +// we move some impls outside so we can easily use them with `docify`. +impl Runtime { + #[docify::export] + fn impl_slot_duration() -> sp_consensus_aura::SlotDuration { + sp_consensus_aura::SlotDuration::from_millis(SLOT_DURATION) + } + + #[docify::export] + fn impl_can_build_upon( + included_hash: ::Hash, + slot: cumulus_primitives_aura::Slot, + ) -> bool { + ConsensusHook::can_build_upon(included_hash, slot) + } +} + impl_runtime_apis! { impl sp_consensus_aura::AuraApi for Runtime { fn slot_duration() -> sp_consensus_aura::SlotDuration { - sp_consensus_aura::SlotDuration::from_millis(SLOT_DURATION) + Runtime::impl_slot_duration() } fn authorities() -> Vec { @@ -63,7 +79,7 @@ impl_runtime_apis! { included_hash: ::Hash, slot: cumulus_primitives_aura::Slot, ) -> bool { - ConsensusHook::can_build_upon(included_hash, slot) + Runtime::impl_can_build_upon(included_hash, slot) } } diff --git a/templates/parachain/runtime/src/configs/mod.rs b/templates/parachain/runtime/src/configs/mod.rs index 8a410a27e4a7..204e74a11e6a 100644 --- a/templates/parachain/runtime/src/configs/mod.rs +++ b/templates/parachain/runtime/src/configs/mod.rs @@ -265,6 +265,7 @@ impl pallet_session::Config for Runtime { type WeightInfo = (); } +#[docify::export(aura_config)] impl pallet_aura::Config for Runtime { type AuthorityId = AuraId; type DisabledValidators = (); diff --git a/templates/parachain/runtime/src/lib.rs b/templates/parachain/runtime/src/lib.rs index 6e6491a19adf..3a9fee38f245 100644 --- a/templates/parachain/runtime/src/lib.rs +++ b/templates/parachain/runtime/src/lib.rs @@ -168,17 +168,21 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { state_version: 1, }; -/// This determines the average expected block time that we are targeting. -/// Blocks will be produced at a minimum duration defined by `SLOT_DURATION`. -/// `SLOT_DURATION` is picked up by `pallet_timestamp` which is in turn picked -/// up by `pallet_aura` to implement `fn slot_duration()`. -/// -/// Change this to adjust the block time. -pub const MILLISECS_PER_BLOCK: u64 = 6000; - -// NOTE: Currently it is not possible to change the slot duration after the chain has started. -// Attempting to do so will brick block production. -pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; +#[docify::export] +mod block_times { + /// This determines the average expected block time that we are targeting. Blocks will be + /// produced at a minimum duration defined by `SLOT_DURATION`. `SLOT_DURATION` is picked up by + /// `pallet_timestamp` which is in turn picked up by `pallet_aura` to implement `fn + /// slot_duration()`. + /// + /// Change this to adjust the block time. + pub const MILLISECS_PER_BLOCK: u64 = 6000; + + // NOTE: Currently it is not possible to change the slot duration after the chain has started. + // Attempting to do so will brick block production. + pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; +} +pub use block_times::*; // Time is measured by number of blocks. pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); @@ -201,21 +205,27 @@ const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(5); /// `Operational` extrinsics. const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); +#[docify::export(max_block_weight)] /// We allow for 2 seconds of compute with a 6 second average block time. const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts( WEIGHT_REF_TIME_PER_SECOND.saturating_mul(2), cumulus_primitives_core::relay_chain::MAX_POV_SIZE as u64, ); -/// Maximum number of blocks simultaneously accepted by the Runtime, not yet included -/// into the relay chain. -const UNINCLUDED_SEGMENT_CAPACITY: u32 = 3; -/// How many parachain blocks are processed by the relay chain per parent. Limits the -/// number of blocks authored per slot. -const BLOCK_PROCESSING_VELOCITY: u32 = 1; -/// Relay chain slot duration, in milliseconds. -const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32 = 6000; +#[docify::export] +mod async_backing_params { + /// Maximum number of blocks simultaneously accepted by the Runtime, not yet included + /// into the relay chain. + pub(crate) const UNINCLUDED_SEGMENT_CAPACITY: u32 = 3; + /// How many parachain blocks are processed by the relay chain per parent. Limits the + /// number of blocks authored per slot. + pub(crate) const BLOCK_PROCESSING_VELOCITY: u32 = 1; + /// Relay chain slot duration, in milliseconds. + pub(crate) const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32 = 6000; +} +pub(crate) use async_backing_params::*; +#[docify::export] /// Aura consensus hook type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< Runtime, @@ -293,6 +303,7 @@ mod runtime { pub type TemplatePallet = pallet_parachain_template; } +#[docify::export(register_validate_block)] cumulus_pallet_parachain_system::register_validate_block! { Runtime = Runtime, BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::,