diff --git a/.gitlab/pipeline/zombienet/polkadot.yml b/.gitlab/pipeline/zombienet/polkadot.yml index 1a1b2b323d0f..fdf0f5d6f968 100644 --- a/.gitlab/pipeline/zombienet/polkadot.yml +++ b/.gitlab/pipeline/zombienet/polkadot.yml @@ -190,35 +190,6 @@ zombienet-polkadot-elastic-scaling-0002-elastic-scaling-doesnt-break-parachains: --local-dir="${LOCAL_DIR}/elastic_scaling" --test="0002-elastic-scaling-doesnt-break-parachains.zndsl" -zombienet-polkadot-elastic-scaling-0003-slot-based-3cores: - extends: - - .zombienet-polkadot-common - variables: - FORCED_INFRA_INSTANCE: "spot-iops" - before_script: - - !reference [ .zombienet-polkadot-common, before_script ] - - cp --remove-destination ${LOCAL_DIR}/assign-core.js ${LOCAL_DIR}/elastic_scaling - # override COL_IMAGE to use test-parachain - - export COL_IMAGE="docker.io/paritypr/test-parachain:${PIPELINE_IMAGE_TAG}" - script: - - /home/nonroot/zombie-net/scripts/ci/run-test-local-env-manager.sh - --local-dir="${LOCAL_DIR}/elastic_scaling" - --test="0003-slot-based-3cores.zndsl" - -zombienet-polkadot-elastic-scaling-0004-mixed-receipt-versions: - extends: - - .zombienet-polkadot-common - variables: - FORCED_INFRA_INSTANCE: "spot-iops" - before_script: - - !reference [ .zombienet-polkadot-common, before_script ] - - cp --remove-destination ${LOCAL_DIR}/assign-core.js ${LOCAL_DIR}/elastic_scaling - # override COL_IMAGE to use test-parachain - - export COL_IMAGE="docker.io/paritypr/test-parachain:${PIPELINE_IMAGE_TAG}" - script: - - /home/nonroot/zombie-net/scripts/ci/run-test-local-env-manager.sh - --local-dir="${LOCAL_DIR}/elastic_scaling" - --test="0004-mixed-receipt-versions.zndsl" .zombienet-polkadot-functional-0012-spam-statement-distribution-requests: extends: @@ -416,3 +387,19 @@ zombienet-polkadot-malus-0001-dispute-valid: - unset NEXTEST_FAILURE_OUTPUT - unset NEXTEST_SUCCESS_OUTPUT - cargo nextest run --archive-file ./artifacts/polkadot-zombienet-tests.tar.zst --no-capture -- smoke::coretime_revenue::coretime_revenue_test + +zombienet-polkadot-coretime-revenue: + 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_3cores::slot_based_3cores_test diff --git a/polkadot/zombienet-sdk-tests/build.rs b/polkadot/zombienet-sdk-tests/build.rs index 240d86386af2..57c5eac5d124 100644 --- a/polkadot/zombienet-sdk-tests/build.rs +++ b/polkadot/zombienet-sdk-tests/build.rs @@ -25,39 +25,47 @@ fn make_env_key(k: &str) -> String { replace_dashes(&k.to_ascii_uppercase()) } +fn wasm_sub_path(chain: &str) -> String { + let (package, runtime_name) = + if let Some(cumulus_test_runtime) = chain.strip_prefix("cumulus-test-runtime-") { + ( + "cumulus-test-runtime".to_string(), + format!("wasm_binary_{}.rs", replace_dashes(cumulus_test_runtime)), + ) + } else { + (format!("{chain}-runtime"), replace_dashes(&format!("{chain}-runtime"))) + }; + + format!("{}/{}.wasm", package, runtime_name) +} + fn find_wasm(chain: &str) -> Option { const PROFILES: [&str; 2] = ["release", "testnet"]; let manifest_path = env::var("CARGO_WORKSPACE_ROOT_DIR").unwrap(); let manifest_path = manifest_path.strip_suffix('/').unwrap(); debug_output!("manifest_path is : {}", manifest_path); - let package = format!("{chain}-runtime"); + + let sub_path = wasm_sub_path(chain); + let profile = PROFILES.into_iter().find(|p| { - let full_path = format!( - "{}/target/{}/wbuild/{}/{}.wasm", - manifest_path, - p, - &package, - replace_dashes(&package) - ); + let full_path = format!("{}/target/{}/wbuild/{}", manifest_path, p, sub_path); debug_output!("checking wasm at : {}", full_path); matches!(path::PathBuf::from(&full_path).try_exists(), Ok(true)) }); debug_output!("profile is : {:?}", profile); profile.map(|profile| { - PathBuf::from(&format!( - "{}/target/{}/wbuild/{}/{}.wasm", - manifest_path, - profile, - &package, - replace_dashes(&package) - )) + PathBuf::from(&format!("{}/target/{}/wbuild/{}", manifest_path, profile, sub_path)) }) } // based on https://gist.github.com/s0me0ne-unkn0wn/bbd83fe32ce10327086adbf13e750eec fn build_wasm(chain: &str) -> PathBuf { - let package = format!("{chain}-runtime"); + let package = if chain.starts_with("cumulus-test-runtime-") { + String::from("cumulus-test-runtime") + } else { + format!("{chain}-runtime") + }; let cargo = env::var("CARGO").unwrap(); let target = env::var("TARGET").unwrap(); @@ -68,7 +76,7 @@ fn build_wasm(chain: &str) -> PathBuf { "-p", &package, "--profile", - "release", + "testnet", "--target", &target, "--target-dir", @@ -81,11 +89,7 @@ fn build_wasm(chain: &str) -> PathBuf { .status() .unwrap(); - let wasm_path = &format!( - "{target_dir}/{target}/release/wbuild/{}/{}.wasm", - &package, - replace_dashes(&package) - ); + let wasm_path = &format!("{target_dir}/{target}/testnet/wbuild/{}", wasm_sub_path(chain)); PathBuf::from(wasm_path) } @@ -127,6 +131,8 @@ fn main() { let manifest_path = env::var("CARGO_MANIFEST_DIR").unwrap(); const METADATA_DIR: &str = "metadata-files"; const CHAINS: [&str; 2] = ["rococo", "coretime-rococo"]; + const CUMULUS_TEST_RUNTIMES: [&str; 2] = + ["cumulus-test-runtime-elastic-scaling", "cumulus-test-runtime-elastic-scaling-mvp"]; let metadata_path = format!("{manifest_path}/{METADATA_DIR}"); @@ -145,6 +151,21 @@ fn main() { }; } + for chain in CUMULUS_TEST_RUNTIMES { + let full_path = format!("{metadata_path}/{chain}-local.scale"); + let output_path = path::PathBuf::from(&full_path); + + match output_path.try_exists() { + Ok(true) => { + debug_output!("got: {}", full_path); + }, + _ => { + debug_output!("needs: {}", full_path); + fetch_metadata_file(chain, &output_path); + }, + }; + } + substrate_build_script_utils::generate_cargo_keys(); substrate_build_script_utils::rerun_if_git_head_changed(); println!("cargo:rerun-if-changed={}", metadata_path); diff --git a/polkadot/zombienet-sdk-tests/tests/elastic_scaling/mod.rs b/polkadot/zombienet-sdk-tests/tests/elastic_scaling/mod.rs new file mode 100644 index 000000000000..5cb42ab48bb8 --- /dev/null +++ b/polkadot/zombienet-sdk-tests/tests/elastic_scaling/mod.rs @@ -0,0 +1,5 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#[cfg(feature = "zombie-metadata")] +mod slot_based_3cores; diff --git a/polkadot/zombienet-sdk-tests/tests/elastic_scaling/slot_based_3cores.rs b/polkadot/zombienet-sdk-tests/tests/elastic_scaling/slot_based_3cores.rs new file mode 100644 index 000000000000..47245e5bb7a5 --- /dev/null +++ b/polkadot/zombienet-sdk-tests/tests/elastic_scaling/slot_based_3cores.rs @@ -0,0 +1,218 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Test that parachains that use a single slot-based collator with elastic scaling MVP and with +// elastic scaling with RFC103 can achieve full throughput of 3 candidates per block. + +use anyhow::anyhow; +#[subxt::subxt(runtime_metadata_path = "metadata-files/rococo-local.scale")] +pub mod rococo {} + +#[subxt::subxt( + runtime_metadata_path = "metadata-files/cumulus-test-runtime-elastic-scaling-local.scale" +)] +mod elastic_scaling_para {} + +#[subxt::subxt( + runtime_metadata_path = "metadata-files/cumulus-test-runtime-elastic-scaling-mvp-local.scale" +)] +mod elastic_scaling_mvp_para {} + +use rococo::runtime_types::{ + pallet_broker::coretime_interface::CoreAssignment, + polkadot_runtime_parachains::assigner_coretime::PartsOf57600, +}; +use serde_json::json; +use std::collections::HashMap; +use subxt::{OnlineClient, PolkadotConfig}; +use subxt_signer::sr25519::dev; +use zombienet_sdk::NetworkConfigBuilder; + +use rococo::{self as rococo_api}; + +#[tokio::test(flavor = "multi_thread")] +async fn slot_based_3cores_test() -> Result<(), anyhow::Error> { + env_logger::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_genesis_overrides(json!({ + "configuration": { + "config": { + "scheduler_params": { + // Num cores is 4, because 2 extra will be added automatically when registering the paras. + "num_cores": 4, + "max_validators_per_core": 1 + }, + "async_backing_params": { + "max_candidate_depth": 6, + "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(&format!("validator-0"))); + + (1..6) + .into_iter() + .fold(r, |acc, i| acc.with_node(|node| node.with_name(&format!("validator-{i}")))) + }) + .with_parachain(|p| { + // Para 2100 uses the old elastic scaling mvp, which doesn't send the new UMP signal + // commitment for selecting the core index. + p.with_id(2100) + .with_default_command("test-parachain") + .with_default_image(images.cumulus.as_str()) + .with_chain("elastic-scaling-mvp") + .with_default_args(vec![("--experimental-use-slot-based").into()]) + .with_collator(|n| n.with_name("collator-elastic-mvp")) + }) + .with_parachain(|p| { + // Para 2200 uses the new RFC103-enabled collator which sens the UMP signal commitment + // for selecting the core index + p.with_id(2200) + .with_default_command("test-parachain") + .with_default_image(images.cumulus.as_str()) + .with_chain("elastic-scaling") + .with_default_args(vec![("--experimental-use-slot-based").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 relay_client: OnlineClient = relay_node.wait_client().await?; + let alice = dev::alice(); + + // Assign two extra cores to each parachain. + relay_client + .tx() + .sign_and_submit_then_watch_default( + &rococo_api::tx().sudo().sudo( + rococo::runtime_types::rococo_runtime::RuntimeCall::Utility( + rococo::runtime_types::pallet_utility::pallet::Call::batch { + calls: vec![ + rococo::runtime_types::rococo_runtime::RuntimeCall::Coretime( + rococo::runtime_types::polkadot_runtime_parachains::coretime::pallet::Call::assign_core { + core: 0, + begin: 0, + assignment: vec![(CoreAssignment::Task(2100), PartsOf57600(57600))], + end_hint: None + } + ), + rococo::runtime_types::rococo_runtime::RuntimeCall::Coretime( + rococo::runtime_types::polkadot_runtime_parachains::coretime::pallet::Call::assign_core { + core: 1, + begin: 0, + assignment: vec![(CoreAssignment::Task(2100), PartsOf57600(57600))], + end_hint: None + } + ), + rococo::runtime_types::rococo_runtime::RuntimeCall::Coretime( + rococo::runtime_types::polkadot_runtime_parachains::coretime::pallet::Call::assign_core { + core: 2, + begin: 0, + assignment: vec![(CoreAssignment::Task(2200), PartsOf57600(57600))], + end_hint: None + } + ), + rococo::runtime_types::rococo_runtime::RuntimeCall::Coretime( + rococo::runtime_types::polkadot_runtime_parachains::coretime::pallet::Call::assign_core { + core: 3, + begin: 0, + assignment: vec![(CoreAssignment::Task(2200), PartsOf57600(57600))], + end_hint: None + } + ) + ], + }, + ), + ), + &alice, + ) + .await? + .wait_for_finalized_success() + .await?; + + log::info!("2 more cores assigned to each parachain"); + + // Expect a backed candidate count of 40 for each parachain in 15 relay chain blocks (2.66 + // 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, [(2100, 40), (2200, 40)].into_iter().collect()) + .await?; + + log::info!("Test finished successfully"); + + Ok(()) +} + +async fn assert_para_throughput( + relay_client: &OnlineClient, + stop_at: u32, + expected_candidate_counts: 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_count) in expected_candidate_counts { + let actual = *candidate_count + .get(¶_id) + .expect("ParaId did not have any backed candidates"); + assert!( + actual >= expected_candidate_count, + "Expected {expected_candidate_count} lower than actual {actual}" + ); + } + + Ok(()) +} diff --git a/polkadot/zombienet-sdk-tests/tests/lib.rs b/polkadot/zombienet-sdk-tests/tests/lib.rs index 74cdc0765600..288aa13844d6 100644 --- a/polkadot/zombienet-sdk-tests/tests/lib.rs +++ b/polkadot/zombienet-sdk-tests/tests/lib.rs @@ -1,4 +1,5 @@ // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 +mod elastic_scaling; mod smoke; diff --git a/polkadot/zombienet-sdk-tests/tests/smoke/coretime_revenue.rs b/polkadot/zombienet-sdk-tests/tests/smoke/coretime_revenue.rs index 7880dc782d05..e57be84be3f3 100644 --- a/polkadot/zombienet-sdk-tests/tests/smoke/coretime_revenue.rs +++ b/polkadot/zombienet-sdk-tests/tests/smoke/coretime_revenue.rs @@ -499,7 +499,7 @@ async fn coretime_revenue_test() -> Result<(), anyhow::Error> { assert_total_issuance(relay_client.clone(), para_client.clone(), total_issuance).await; - log::info!("Test finished successfuly"); + log::info!("Test finished successfully"); Ok(()) }