diff --git a/.github/workflows/cargo-tests.yml b/.github/workflows/cargo-tests.yml index fdb5cad..2dc22ba 100644 --- a/.github/workflows/cargo-tests.yml +++ b/.github/workflows/cargo-tests.yml @@ -74,6 +74,9 @@ jobs: - name: Check benchmarking compilation run: cargo check --release --features runtime-benchmarks + - name: Run tests with async backing + run: cargo test --release --features="async-backing" + - name: Check clippy run: cargo clippy --release --locked --all-targets -- -D warnings diff --git a/Cargo.lock b/Cargo.lock index b0ac510..3e25c83 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7217,6 +7217,7 @@ dependencies = [ "cumulus-pallet-session-benchmarking", "cumulus-pallet-xcm", "cumulus-pallet-xcmp-queue", + "cumulus-primitives-aura", "cumulus-primitives-core", "cumulus-primitives-utility", "frame-benchmarking", diff --git a/Cargo.toml b/Cargo.toml index 1767137..0e57fb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -118,6 +118,7 @@ cumulus-pallet-parachain-system = { git = "https://github.com/paritytech/polkado cumulus-pallet-session-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-v1.7.0" } cumulus-pallet-xcm = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-v1.7.0" } cumulus-pallet-xcmp-queue = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-v1.7.0" } +cumulus-primitives-aura = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-v1.7.0" } cumulus-primitives-core = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-v1.7.0" } cumulus-primitives-parachain-inherent = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-v1.7.0" } cumulus-primitives-utility = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-v1.7.0" } diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 36062ec..a771a2c 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -1,5 +1,6 @@ * General Guides ** xref:guides/weights_fees.adoc[Weights & Fees] +** xref:guides/async_backing.adoc[Async Backing] * Runtimes ** xref:runtimes/generic.adoc[Generic Runtime] * Runtime Descriptions diff --git a/docs/modules/ROOT/pages/guides/async_backing.adoc b/docs/modules/ROOT/pages/guides/async_backing.adoc new file mode 100644 index 0000000..8e31681 --- /dev/null +++ b/docs/modules/ROOT/pages/guides/async_backing.adoc @@ -0,0 +1,36 @@ +:source-highlighter: highlight.js +:highlightjs-languages: rust +:github-icon: pass:[] + += Async Backing + +Async backing is a feature of parachains that allow to shorten the block time, get more execution time and speed up transaction inclusion in general. You can read in details what async backing is in the link:https://wiki.polkadot.network/docs/learn-async-backing#candidate-receipt[general guide] and in the link:https://wiki.polkadot.network/docs/maintain-guides-async-backing[update guide]. In our generic template we have included async backing under a feature. This page describes how can you build and test the template with async backing enabled. + +== How to build + +Async backing is enabled by the feature `async-backing`. You can build the template with it like this: + +```bash +cargo build --release --features="async-backing" +``` + +== How to test + +You can always test it against Rococo (it should already have enabled async backing), but the fastest way is to test against a local relay chain. In our repository you can find a script, a config and a link:https://github.com/OpenZeppelin/polkadot-runtime-template/tree/main/zombienet-config[manual] that will run both relay chain and a parachain. To launch a parachain with a relay chain, you will need to run these commands: + +* Get the Polkadot binaries: +** If you are using some Linux distro, you can download the binaries: ++ +``` +./scripts/zombienet.sh init +``` +** Otherwise, you have to build it from scratch. It takes ++ +``` +./scripts/zombienet.sh build +``` +* After that, launch the network: ++ +``` +./scripts/zombienet.sh devnet +``` \ No newline at end of file diff --git a/node/Cargo.toml b/node/Cargo.toml index e4efa6f..5f0a36f 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -75,6 +75,8 @@ substrate-build-script-utils = { workspace = true } [features] default = [] +async-backing = [] +experimental = [] runtime-benchmarks = [ "frame-benchmarking-cli/runtime-benchmarks", "frame-benchmarking/runtime-benchmarks", diff --git a/node/src/service.rs b/node/src/service.rs index 4de3380..1e2c096 100644 --- a/node/src/service.rs +++ b/node/src/service.rs @@ -13,6 +13,8 @@ use cumulus_client_service::{ build_network, build_relay_chain_interface, prepare_node_config, start_relay_chain_tasks, BuildNetworkParams, CollatorSybilResistance, DARecoveryProfile, StartRelayChainTasksParams, }; +#[cfg(feature = "async-backing")] +use cumulus_primitives_core::relay_chain::ValidationCode; use cumulus_primitives_core::{relay_chain::CollatorPair, ParaId}; use cumulus_relay_chain_interface::{OverseerHandle, RelayChainInterface}; // Substrate Imports @@ -243,7 +245,10 @@ async fn start_node_impl( task_manager: &mut task_manager, config: parachain_config, keystore: params.keystore_container.keystore(), + #[cfg(not(feature = "async-backing"))] backend, + #[cfg(feature = "async-backing")] + backend: backend.clone(), network: network.clone(), sync_service: sync_service.clone(), system_rpc_tx, @@ -309,6 +314,8 @@ async fn start_node_impl( if validator { start_consensus( client.clone(), + #[cfg(feature = "async-backing")] + backend.clone(), block_import, prometheus_registry.as_ref(), telemetry.as_ref().map(|t| t.handle()), @@ -362,6 +369,7 @@ fn build_import_queue( fn start_consensus( client: Arc, + #[cfg(feature = "async-backing")] backend: Arc, block_import: ParachainBlockImport, prometheus_registry: Option<&Registry>, telemetry: Option, @@ -376,13 +384,13 @@ fn start_consensus( overseer_handle: OverseerHandle, announce_block: Arc>) + Send + Sync>, ) -> Result<(), sc_service::Error> { - use cumulus_client_consensus_aura::collators::basic::{ - self as basic_aura, Params as BasicAuraParams, - }; + #[cfg(not(feature = "async-backing"))] + use cumulus_client_consensus_aura::collators::basic::{self as basic_aura, Params}; + #[cfg(feature = "async-backing")] + use cumulus_client_consensus_aura::collators::lookahead::{self as aura, Params}; // NOTE: because we use Aura here explicitly, we can use // `CollatorSybilResistance::Resistant` when starting the network. - let slot_duration = cumulus_client_consensus_aura::slot_duration(&*client)?; let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( @@ -402,11 +410,20 @@ fn start_consensus( client.clone(), ); - let params = BasicAuraParams { + let params = Params { create_inherent_data_providers: move |_, ()| async move { Ok(()) }, block_import, + #[cfg(not(feature = "async-backing"))] para_client: client, + #[cfg(feature = "async-backing")] + para_client: client.clone(), + #[cfg(feature = "async-backing")] + para_backend: backend, relay_client: relay_chain_interface, + #[cfg(feature = "async-backing")] + code_hash_provider: move |block_hash| { + client.code_at(block_hash).ok().map(ValidationCode).map(|c| c.hash()) + }, sync_oracle, keystore, collator_key, @@ -417,14 +434,26 @@ fn start_consensus( proposer, collator_service, // Very limited proposal time. + #[cfg(not(feature = "async-backing"))] authoring_duration: Duration::from_millis(500), + #[cfg(feature = "async-backing")] + authoring_duration: Duration::from_millis(1500), + #[cfg(not(feature = "async-backing"))] collation_request_receiver: None, + #[cfg(feature = "async-backing")] + reinitialize: false, }; + #[cfg(not(feature = "async-backing"))] let fut = basic_aura::run::( params, ); + #[cfg(feature = "async-backing")] + let fut = + aura::run::( + params, + ); task_manager.spawn_essential_handle().spawn("aura", None, fut); Ok(()) diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 14bc210..809f435 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -75,6 +75,7 @@ cumulus-pallet-parachain-system = { workspace = true, features = [ cumulus-pallet-session-benchmarking = { workspace = true } cumulus-pallet-xcm = { workspace = true } cumulus-pallet-xcmp-queue = { workspace = true } +cumulus-primitives-aura = { workspace = true } cumulus-primitives-core = { workspace = true } cumulus-primitives-utility = { workspace = true } pallet-collator-selection = { workspace = true } @@ -150,6 +151,7 @@ std = [ "xcm-executor/std", "xcm/std", ] +async-backing = [] runtime-benchmarks = [ "assets-common/runtime-benchmarks", diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index d2492b3..2a81a99 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -11,6 +11,9 @@ pub mod governance; mod weights; pub mod xcm_config; +#[cfg(feature = "async-backing")] +use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; +#[cfg(not(feature = "async-backing"))] use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; use frame_support::{ @@ -221,6 +224,9 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { /// up by `pallet_aura` to implement `fn slot_duration()`. /// /// Change this to adjust the block time. +#[cfg(feature = "async-backing")] +pub const MILLISECS_PER_BLOCK: u64 = 6000; +#[cfg(not(feature = "async-backing"))] pub const MILLISECS_PER_BLOCK: u64 = 12000; // NOTE: Currently it is not possible to change the slot duration after the @@ -242,12 +248,18 @@ pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); /// We allow for 0.5 of a second of compute with a 12 second average block time. pub const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts( + #[cfg(feature = "async-backing")] + WEIGHT_REF_TIME_PER_SECOND.saturating_mul(2), + #[cfg(not(feature = "async-backing"))] WEIGHT_REF_TIME_PER_SECOND.saturating_div(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. +#[cfg(feature = "async-backing")] +pub const UNINCLUDED_SEGMENT_CAPACITY: u32 = 3; +#[cfg(not(feature = "async-backing"))] 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. @@ -399,6 +411,9 @@ impl pallet_preimage::Config for Runtime { } impl pallet_timestamp::Config for Runtime { + #[cfg(feature = "experimental")] + type MinimumPeriod = ConstU64<0>; + #[cfg(not(feature = "experimental"))] type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>; /// A timestamp: milliseconds since the unix epoch. type Moment = u64; @@ -565,14 +580,19 @@ parameter_types! { pub const RelayOrigin: AggregateMessageOrigin = AggregateMessageOrigin::Parent; } +type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< + Runtime, + RELAY_CHAIN_SLOT_DURATION_MILLIS, + BLOCK_PROCESSING_VELOCITY, + UNINCLUDED_SEGMENT_CAPACITY, +>; + impl cumulus_pallet_parachain_system::Config for Runtime { + #[cfg(not(feature = "async-backing"))] type CheckAssociatedRelayNumber = RelayNumberStrictlyIncreases; - type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< - Runtime, - RELAY_CHAIN_SLOT_DURATION_MILLIS, - BLOCK_PROCESSING_VELOCITY, - UNINCLUDED_SEGMENT_CAPACITY, - >; + #[cfg(feature = "async-backing")] + type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases; + type ConsensusHook = ConsensusHook; type DmpQueue = frame_support::traits::EnqueueWithOrigin; type OnSystemEvent = (); type OutboundXcmpMessageSource = XcmpQueue; @@ -670,17 +690,26 @@ impl pallet_session::Config for Runtime { type WeightInfo = pallet_session::weights::SubstrateWeight; } +#[cfg(not(feature = "async-backing"))] parameter_types! { pub const AllowMultipleBlocksPerSlot: bool = false; pub const MaxAuthorities: u32 = 100_000; } +#[cfg(feature = "async-backing")] +parameter_types! { + pub const AllowMultipleBlocksPerSlot: bool = true; + pub const MaxAuthorities: u32 = 100_000; +} + impl pallet_aura::Config for Runtime { type AllowMultipleBlocksPerSlot = AllowMultipleBlocksPerSlot; type AuthorityId = AuraId; type DisabledValidators = (); type MaxAuthorities = MaxAuthorities; - #[cfg(feature = "experimental")] + #[cfg(all(feature = "experimental", feature = "async-backing"))] + type SlotDuration = ConstU64; + #[cfg(all(feature = "experimental", not(feature = "async-backing")))] type SlotDuration = pallet_aura::MinimumPeriodTimesTwo; } @@ -843,6 +872,9 @@ mod benches { impl_runtime_apis! { impl sp_consensus_aura::AuraApi for Runtime { fn slot_duration() -> sp_consensus_aura::SlotDuration { + #[cfg(feature = "async-backing")] + return sp_consensus_aura::SlotDuration::from_millis(SLOT_DURATION); + #[cfg(not(feature = "async-backing"))] sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration()) } @@ -984,6 +1016,16 @@ impl_runtime_apis! { } } + #[cfg(feature = "async-backing")] + impl cumulus_primitives_aura::AuraUnincludedSegmentApi for Runtime { + fn can_build_upon( + included_hash: ::Hash, + slot: cumulus_primitives_aura::Slot + ) -> bool { + ConsensusHook::can_build_upon(included_hash, slot) + } + } + #[cfg(feature = "try-runtime")] impl frame_try_runtime::TryRuntime for Runtime { fn on_runtime_upgrade(checks: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) { diff --git a/runtime/tests/constants_test.rs b/runtime/tests/constants_test.rs index d8aa28c..03ed89f 100644 --- a/runtime/tests/constants_test.rs +++ b/runtime/tests/constants_test.rs @@ -34,6 +34,7 @@ mod runtime_tests { #[test] fn frame_system_constants() { + #[cfg(not(feature = "async-backing"))] assert_eq!( MAXIMUM_BLOCK_WEIGHT, Weight::from_parts( @@ -42,17 +43,31 @@ mod runtime_tests { ) ); + #[cfg(feature = "async-backing")] + assert_eq!( + MAXIMUM_BLOCK_WEIGHT, + Weight::from_parts( + frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND.saturating_mul(2), + cumulus_primitives_core::relay_chain::MAX_POV_SIZE as u64 + ) + ); + assert_eq!(AVERAGE_ON_INITIALIZE_RATIO, Perbill::from_percent(5)); assert_eq!(NORMAL_DISPATCH_RATIO, Perbill::from_percent(75)); - + #[cfg(not(feature = "async-backing"))] assert_eq!(UNINCLUDED_SEGMENT_CAPACITY, 1); + #[cfg(feature = "async-backing")] + assert_eq!(UNINCLUDED_SEGMENT_CAPACITY, 3); assert_eq!(BLOCK_PROCESSING_VELOCITY, 1); assert_eq!(RELAY_CHAIN_SLOT_DURATION_MILLIS, 6000); + #[cfg(not(feature = "async-backing"))] assert_eq!(MILLISECS_PER_BLOCK, 12000); + #[cfg(feature = "async-backing")] + assert_eq!(MILLISECS_PER_BLOCK, 6000); assert_eq!(SLOT_DURATION, MILLISECS_PER_BLOCK); @@ -154,7 +169,10 @@ mod runtime_tests { #[test] #[allow(clippy::assertions_on_constants)] fn aura_constants() { + #[cfg(not(feature = "async-backing"))] assert!(!AllowMultipleBlocksPerSlot::get()); + #[cfg(feature = "async-backing")] + assert!(AllowMultipleBlocksPerSlot::get()); assert_eq!(MaxAuthorities::get(), 100_000); }