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/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. diff --git a/cumulus/test/runtime/Cargo.toml b/cumulus/test/runtime/Cargo.toml index 150838e5e96e..5d9d12c8af7c 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..72e54aa186c2 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,21 +102,27 @@ 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(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(not(feature = "elastic-scaling"))] -pub const MILLISECS_PER_BLOCK: u64 = 6000; -#[cfg(feature = "elastic-scaling")] +#[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(feature = "elastic-scaling-500ms")] +pub const MILLISECS_PER_BLOCK: u64 = 500; + pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32 = 6000; 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/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(()) +}