From 1f12f725b811c8e3beda869370f0b4f8c3d64762 Mon Sep 17 00:00:00 2001 From: alindima Date: Fri, 20 Dec 2024 12:38:45 +0200 Subject: [PATCH 1/3] cumulus: update PARENT_SEARCH_DEPTH --- cumulus/client/consensus/aura/src/collators/mod.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cumulus/client/consensus/aura/src/collators/mod.rs b/cumulus/client/consensus/aura/src/collators/mod.rs index 89070607fbab..e27520db8f5c 100644 --- a/cumulus/client/consensus/aura/src/collators/mod.rs +++ b/cumulus/client/consensus/aura/src/collators/mod.rs @@ -44,12 +44,13 @@ pub mod lookahead; pub mod slot_based; // This is an arbitrary value which is likely guaranteed to exceed any reasonable -// limit, as it would correspond to 10 non-included blocks. +// limit, as it would correspond to 30 non-included blocks. // // Since we only search for parent blocks which have already been imported, // we can guarantee that all imported blocks respect the unincluded segment -// rules specified by the parachain's runtime and thus will never be too deep. -const PARENT_SEARCH_DEPTH: usize = 10; +// rules specified by the parachain's runtime and thus will never be too deep. This is just an extra +// sanity check. +const PARENT_SEARCH_DEPTH: usize = 30; /// Check the `local_validation_code_hash` against the validation code hash in the relay chain /// state. From f5d2c486691f9ee0d7b40f73b0b23367370f828f Mon Sep 17 00:00:00 2001 From: alindima Date: Mon, 23 Dec 2024 11:30:01 +0200 Subject: [PATCH 2/3] add zombienet test --- .gitlab/pipeline/zombienet/polkadot.yml | 16 +++ cumulus/test/runtime/Cargo.toml | 1 + cumulus/test/runtime/build.rs | 8 ++ cumulus/test/runtime/src/lib.rs | 17 ++- cumulus/test/service/src/chain_spec.rs | 10 ++ cumulus/test/service/src/cli.rs | 6 + .../tests/elastic_scaling/helpers.rs | 60 -------- .../tests/elastic_scaling/mod.rs | 1 + .../elastic_scaling/slot_based_12cores.rs | 129 ++++++++++++++++++ 9 files changed, 185 insertions(+), 63 deletions(-) delete mode 100644 polkadot/zombienet-sdk-tests/tests/elastic_scaling/helpers.rs create mode 100644 polkadot/zombienet-sdk-tests/tests/elastic_scaling/slot_based_12cores.rs diff --git a/.gitlab/pipeline/zombienet/polkadot.yml b/.gitlab/pipeline/zombienet/polkadot.yml index 831a0b12106c..f6940f06fb49 100644 --- a/.gitlab/pipeline/zombienet/polkadot.yml +++ b/.gitlab/pipeline/zombienet/polkadot.yml @@ -376,6 +376,22 @@ zombienet-polkadot-elastic-scaling-slot-based-3cores: - unset NEXTEST_SUCCESS_OUTPUT - cargo nextest run --archive-file ./artifacts/polkadot-zombienet-tests.tar.zst --no-capture -- elastic_scaling::slot_based_3cores::slot_based_3cores_test +zombienet-polkadot-elastic-scaling-slot-based-12cores: + extends: + - .zombienet-polkadot-common + needs: + - job: build-polkadot-zombienet-tests + artifacts: true + before_script: + - !reference [ ".zombienet-polkadot-common", "before_script" ] + - export POLKADOT_IMAGE="${ZOMBIENET_INTEGRATION_TEST_IMAGE}" + - export CUMULUS_IMAGE="docker.io/paritypr/test-parachain:${PIPELINE_IMAGE_TAG}" + script: + # we want to use `--no-capture` in zombienet tests. + - unset NEXTEST_FAILURE_OUTPUT + - unset NEXTEST_SUCCESS_OUTPUT + - cargo nextest run --archive-file ./artifacts/polkadot-zombienet-tests.tar.zst --no-capture -- elastic_scaling::slot_based_12cores::slot_based_12cores_test + zombienet-polkadot-elastic-scaling-doesnt-break-parachains: extends: - .zombienet-polkadot-common diff --git a/cumulus/test/runtime/Cargo.toml b/cumulus/test/runtime/Cargo.toml index b80170af3e83..143494cd120e 100644 --- a/cumulus/test/runtime/Cargo.toml +++ b/cumulus/test/runtime/Cargo.toml @@ -96,4 +96,5 @@ std = [ ] increment-spec-version = [] elastic-scaling = [] +elastic-scaling-500ms = [] experimental-ump-signals = ["cumulus-pallet-parachain-system/experimental-ump-signals"] diff --git a/cumulus/test/runtime/build.rs b/cumulus/test/runtime/build.rs index 43e60c1074a0..99d30ce6dc37 100644 --- a/cumulus/test/runtime/build.rs +++ b/cumulus/test/runtime/build.rs @@ -39,6 +39,14 @@ fn main() { .import_memory() .set_file_name("wasm_binary_elastic_scaling.rs") .build(); + + WasmBuilder::new() + .with_current_project() + .enable_feature("elastic-scaling-500ms") + .enable_feature("experimental-ump-signals") + .import_memory() + .set_file_name("wasm_binary_elastic_scaling_500ms.rs") + .build(); } #[cfg(not(feature = "std"))] diff --git a/cumulus/test/runtime/src/lib.rs b/cumulus/test/runtime/src/lib.rs index 4abc10276af1..d2f74500b0a5 100644 --- a/cumulus/test/runtime/src/lib.rs +++ b/cumulus/test/runtime/src/lib.rs @@ -27,6 +27,10 @@ pub mod wasm_spec_version_incremented { include!(concat!(env!("OUT_DIR"), "/wasm_binary_spec_version_incremented.rs")); } +pub mod elastic_scaling_500ms { + #[cfg(feature = "std")] + include!(concat!(env!("OUT_DIR"), "/wasm_binary_elastic_scaling_500ms.rs")); +} pub mod elastic_scaling_mvp { #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary_elastic_scaling_mvp.rs")); @@ -98,9 +102,9 @@ impl_opaque_keys! { /// The para-id used in this runtime. pub const PARACHAIN_ID: u32 = 100; -#[cfg(not(feature = "elastic-scaling"))] +#[cfg(not(any(feature = "elastic-scaling", feature = "elastic-scaling-500ms")))] const UNINCLUDED_SEGMENT_CAPACITY: u32 = 4; -#[cfg(not(feature = "elastic-scaling"))] +#[cfg(not(any(feature = "elastic-scaling", feature = "elastic-scaling-500ms")))] const BLOCK_PROCESSING_VELOCITY: u32 = 1; #[cfg(feature = "elastic-scaling")] @@ -108,10 +112,17 @@ const UNINCLUDED_SEGMENT_CAPACITY: u32 = 7; #[cfg(feature = "elastic-scaling")] const BLOCK_PROCESSING_VELOCITY: u32 = 4; -#[cfg(not(feature = "elastic-scaling"))] +#[cfg(feature = "elastic-scaling-500ms")] +const UNINCLUDED_SEGMENT_CAPACITY: u32 = 30; +#[cfg(feature = "elastic-scaling-500ms")] +const BLOCK_PROCESSING_VELOCITY: u32 = 12; + +#[cfg(not(any(feature = "elastic-scaling", feature = "elastic-scaling-500ms")))] pub const MILLISECS_PER_BLOCK: u64 = 6000; #[cfg(feature = "elastic-scaling")] pub const MILLISECS_PER_BLOCK: u64 = 2000; +#[cfg(feature = "elastic-scaling-500ms")] +pub const MILLISECS_PER_BLOCK: u64 = 500; pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; diff --git a/cumulus/test/service/src/chain_spec.rs b/cumulus/test/service/src/chain_spec.rs index 5ebcc14592d7..b59bd7ab46bd 100644 --- a/cumulus/test/service/src/chain_spec.rs +++ b/cumulus/test/service/src/chain_spec.rs @@ -117,6 +117,16 @@ pub fn get_elastic_scaling_chain_spec(id: Option) -> ChainSpec { ) } +/// Get the chain spec for a specific parachain ID. +pub fn get_elastic_scaling_500ms_chain_spec(id: Option) -> ChainSpec { + get_chain_spec_with_extra_endowed( + id, + Default::default(), + cumulus_test_runtime::elastic_scaling_500ms::WASM_BINARY + .expect("WASM binary was not built, please build it!"), + ) +} + /// Get the chain spec for a specific parachain ID. pub fn get_elastic_scaling_mvp_chain_spec(id: Option) -> ChainSpec { get_chain_spec_with_extra_endowed( diff --git a/cumulus/test/service/src/cli.rs b/cumulus/test/service/src/cli.rs index e019089e70fe..7909ffbf7142 100644 --- a/cumulus/test/service/src/cli.rs +++ b/cumulus/test/service/src/cli.rs @@ -274,6 +274,12 @@ impl SubstrateCli for TestCollatorCli { 2200, )))) as Box<_> }, + "elastic-scaling-500ms" => { + tracing::info!("Using elastic-scaling 500ms chain spec."); + Box::new(cumulus_test_service::get_elastic_scaling_500ms_chain_spec(Some( + ParaId::from(2300), + ))) as Box<_> + }, path => { let chain_spec = cumulus_test_service::chain_spec::ChainSpec::from_json_file(path.into())?; diff --git a/polkadot/zombienet-sdk-tests/tests/elastic_scaling/helpers.rs b/polkadot/zombienet-sdk-tests/tests/elastic_scaling/helpers.rs deleted file mode 100644 index 7d4ad4a1dd8b..000000000000 --- a/polkadot/zombienet-sdk-tests/tests/elastic_scaling/helpers.rs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -use super::rococo; -use std::{collections::HashMap, ops::Range}; -use subxt::{OnlineClient, PolkadotConfig}; - -// Helper function for asserting the throughput of parachains (total number of backed candidates in -// a window of relay chain blocks), after the first session change. -pub async fn assert_para_throughput( - relay_client: &OnlineClient, - stop_at: u32, - expected_candidate_ranges: HashMap>, -) -> Result<(), anyhow::Error> { - let mut blocks_sub = relay_client.blocks().subscribe_finalized().await?; - let mut candidate_count: HashMap = HashMap::new(); - let mut current_block_count = 0; - let mut had_first_session_change = false; - - while let Some(block) = blocks_sub.next().await { - let block = block?; - log::debug!("Finalized relay chain block {}", block.number()); - let events = block.events().await?; - let is_session_change = events.has::()?; - - if !had_first_session_change && is_session_change { - had_first_session_change = true; - } - - if had_first_session_change && !is_session_change { - current_block_count += 1; - - for event in events.find::() { - *(candidate_count.entry(event?.0.descriptor.para_id.0).or_default()) += 1; - } - } - - if current_block_count == stop_at { - break; - } - } - - log::info!( - "Reached {} finalized relay chain blocks that contain backed candidates. The per-parachain distribution is: {:#?}", - stop_at, - candidate_count - ); - - for (para_id, expected_candidate_range) in expected_candidate_ranges { - let actual = candidate_count - .get(¶_id) - .expect("ParaId did not have any backed candidates"); - assert!( - expected_candidate_range.contains(actual), - "Candidate count {actual} not within range {expected_candidate_range:?}" - ); - } - - Ok(()) -} diff --git a/polkadot/zombienet-sdk-tests/tests/elastic_scaling/mod.rs b/polkadot/zombienet-sdk-tests/tests/elastic_scaling/mod.rs index 9cfd5db5a096..a993e8e27214 100644 --- a/polkadot/zombienet-sdk-tests/tests/elastic_scaling/mod.rs +++ b/polkadot/zombienet-sdk-tests/tests/elastic_scaling/mod.rs @@ -3,4 +3,5 @@ mod basic_3cores; mod doesnt_break_parachains; +mod slot_based_12cores; mod slot_based_3cores; diff --git a/polkadot/zombienet-sdk-tests/tests/elastic_scaling/slot_based_12cores.rs b/polkadot/zombienet-sdk-tests/tests/elastic_scaling/slot_based_12cores.rs new file mode 100644 index 000000000000..4d0e1adad084 --- /dev/null +++ b/polkadot/zombienet-sdk-tests/tests/elastic_scaling/slot_based_12cores.rs @@ -0,0 +1,129 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Test that a parachain that uses a single slot-based collator with elastic scaling can use 12 +// cores in order to achieve 500ms blocks. + +use anyhow::anyhow; + +use crate::helpers::{ + assert_finalized_block_height, assert_para_throughput, rococo, + rococo::runtime_types::{ + pallet_broker::coretime_interface::CoreAssignment, + polkadot_runtime_parachains::assigner_coretime::PartsOf57600, + }, +}; +use polkadot_primitives::Id as ParaId; +use serde_json::json; +use subxt::{OnlineClient, PolkadotConfig}; +use subxt_signer::sr25519::dev; +use zombienet_sdk::NetworkConfigBuilder; + +#[tokio::test(flavor = "multi_thread")] +async fn slot_based_12cores_test() -> Result<(), anyhow::Error> { + let _ = env_logger::try_init_from_env( + env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"), + ); + + let images = zombienet_sdk::environment::get_images_from_env(); + + let config = NetworkConfigBuilder::new() + .with_relaychain(|r| { + let r = r + .with_chain("rococo-local") + .with_default_command("polkadot") + .with_default_image(images.polkadot.as_str()) + .with_default_args(vec![("-lparachain=debug").into()]) + .with_genesis_overrides(json!({ + "configuration": { + "config": { + "scheduler_params": { + "num_cores": 11, + "max_validators_per_core": 1 + }, + "async_backing_params": { + "max_candidate_depth": 24, + "allowed_ancestry_len": 2 + } + } + } + })) + // Have to set a `with_node` outside of the loop below, so that `r` has the right + // type. + .with_node(|node| node.with_name("validator-0")); + + (1..12) + .fold(r, |acc, i| acc.with_node(|node| node.with_name(&format!("validator-{i}")))) + }) + .with_parachain(|p| { + p.with_id(2300) + .with_default_command("test-parachain") + .with_default_image(images.cumulus.as_str()) + .with_chain("elastic-scaling-500ms") + .with_default_args(vec![ + ("--experimental-use-slot-based").into(), + ("-lparachain=debug,aura=debug").into(), + ]) + .with_collator(|n| n.with_name("collator-elastic")) + }) + .build() + .map_err(|e| { + let errs = e.into_iter().map(|e| e.to_string()).collect::>().join(" "); + anyhow!("config errs: {errs}") + })?; + + let spawn_fn = zombienet_sdk::environment::get_spawn_fn(); + let network = spawn_fn(config).await?; + + let relay_node = network.get_node("validator-0")?; + let para_node = network.get_node("collator-elastic")?; + + let relay_client: OnlineClient = relay_node.wait_client().await?; + let alice = dev::alice(); + + // Assign 11 extra cores to the parachain. + + relay_client + .tx() + .sign_and_submit_then_watch_default( + &rococo::tx() + .sudo() + .sudo(rococo::runtime_types::rococo_runtime::RuntimeCall::Utility( + rococo::runtime_types::pallet_utility::pallet::Call::batch { + calls: (0..11).map(|idx| rococo::runtime_types::rococo_runtime::RuntimeCall::Coretime( + rococo::runtime_types::polkadot_runtime_parachains::coretime::pallet::Call::assign_core { + core: idx, + begin: 0, + assignment: vec![(CoreAssignment::Task(2300), PartsOf57600(57600))], + end_hint: None + } + )).collect() + }, + )), + &alice, + ) + .await? + .wait_for_finalized_success() + .await?; + + log::info!("11 more cores assigned to the parachain"); + + // Expect a backed candidate count of at least 170 in 15 relay chain blocks + // (11.33 candidates per para per relay chain block). + // Note that only blocks after the first session change and blocks that don't contain a session + // change will be counted. + assert_para_throughput( + &relay_client, + 15, + [(ParaId::from(2300), 170..181)].into_iter().collect(), + ) + .await?; + + // Assert the parachain finalized block height is also on par with the number of backed + // candidates. + assert_finalized_block_height(¶_node.wait_client().await?, 158..181).await?; + + log::info!("Test finished successfully"); + + Ok(()) +} From c15d5891e67b5a101abc8df79795a59ac9b14ee7 Mon Sep 17 00:00:00 2001 From: alindima Date: Mon, 23 Dec 2024 12:28:17 +0200 Subject: [PATCH 3/3] fix constants --- cumulus/test/runtime/src/lib.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/cumulus/test/runtime/src/lib.rs b/cumulus/test/runtime/src/lib.rs index d2f74500b0a5..72e54aa186c2 100644 --- a/cumulus/test/runtime/src/lib.rs +++ b/cumulus/test/runtime/src/lib.rs @@ -106,21 +106,20 @@ pub const PARACHAIN_ID: u32 = 100; const UNINCLUDED_SEGMENT_CAPACITY: u32 = 4; #[cfg(not(any(feature = "elastic-scaling", feature = "elastic-scaling-500ms")))] const BLOCK_PROCESSING_VELOCITY: u32 = 1; +#[cfg(not(any(feature = "elastic-scaling", feature = "elastic-scaling-500ms")))] +pub const MILLISECS_PER_BLOCK: u64 = 6000; -#[cfg(feature = "elastic-scaling")] +#[cfg(all(feature = "elastic-scaling", not(feature = "elastic-scaling-500ms")))] const UNINCLUDED_SEGMENT_CAPACITY: u32 = 7; -#[cfg(feature = "elastic-scaling")] +#[cfg(all(feature = "elastic-scaling", not(feature = "elastic-scaling-500ms")))] const BLOCK_PROCESSING_VELOCITY: u32 = 4; +#[cfg(all(feature = "elastic-scaling", not(feature = "elastic-scaling-500ms")))] +pub const MILLISECS_PER_BLOCK: u64 = 2000; #[cfg(feature = "elastic-scaling-500ms")] const UNINCLUDED_SEGMENT_CAPACITY: u32 = 30; #[cfg(feature = "elastic-scaling-500ms")] const BLOCK_PROCESSING_VELOCITY: u32 = 12; - -#[cfg(not(any(feature = "elastic-scaling", feature = "elastic-scaling-500ms")))] -pub const MILLISECS_PER_BLOCK: u64 = 6000; -#[cfg(feature = "elastic-scaling")] -pub const MILLISECS_PER_BLOCK: u64 = 2000; #[cfg(feature = "elastic-scaling-500ms")] pub const MILLISECS_PER_BLOCK: u64 = 500;