diff --git a/Cargo.lock b/Cargo.lock index a10a7dcf5..d0156504a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8746,6 +8746,7 @@ dependencies = [ "frame-system", "log", "parity-scale-codec", + "polkadot-runtime-parachains", "rand", "rand_chacha", "scale-info", diff --git a/node/src/chain_spec/dancebox.rs b/node/src/chain_spec/dancebox.rs index 054866e40..709fe17e3 100644 --- a/node/src/chain_spec/dancebox.rs +++ b/node/src/chain_spec/dancebox.rs @@ -97,6 +97,7 @@ pub fn development_config( collators_per_parathread: 1, parathreads_per_collator: 1, target_container_chain_fullness: Perbill::from_percent(80), + max_parachain_cores_percentage: None, }, ..Default::default() }, @@ -159,6 +160,7 @@ pub fn local_dancebox_config( collators_per_parathread: 1, parathreads_per_collator: 1, target_container_chain_fullness: Perbill::from_percent(80), + max_parachain_cores_percentage: None, }, ..Default::default() }, diff --git a/node/src/chain_spec/flashbox.rs b/node/src/chain_spec/flashbox.rs index 939c4351d..555cd6ac4 100644 --- a/node/src/chain_spec/flashbox.rs +++ b/node/src/chain_spec/flashbox.rs @@ -97,6 +97,7 @@ pub fn development_config( collators_per_parathread: 1, parathreads_per_collator: 1, target_container_chain_fullness: Perbill::from_percent(80), + max_parachain_cores_percentage: None, }, ..Default::default() }, @@ -159,6 +160,7 @@ pub fn local_flashbox_config( collators_per_parathread: 1, parathreads_per_collator: 1, target_container_chain_fullness: Perbill::from_percent(80), + max_parachain_cores_percentage: None, }, ..Default::default() }, diff --git a/pallets/collator-assignment/Cargo.toml b/pallets/collator-assignment/Cargo.toml index be98c309e..33ecd2676 100644 --- a/pallets/collator-assignment/Cargo.toml +++ b/pallets/collator-assignment/Cargo.toml @@ -19,6 +19,7 @@ frame-support = { workspace = true } frame-system = { workspace = true } log = { workspace = true } parity-scale-codec = { workspace = true, features = [ "derive", "max-encoded-len" ] } +polkadot-runtime-parachains = { workspace = true } rand = { workspace = true } rand_chacha = { workspace = true } scale-info = { workspace = true } @@ -42,6 +43,7 @@ std = [ "frame-system/std", "log/std", "parity-scale-codec/std", + "polkadot-runtime-parachains/std", "rand/std", "rand_chacha/std", "scale-info/std", diff --git a/pallets/collator-assignment/src/lib.rs b/pallets/collator-assignment/src/lib.rs index 236c15a3d..3fe593570 100644 --- a/pallets/collator-assignment/src/lib.rs +++ b/pallets/collator-assignment/src/lib.rs @@ -41,6 +41,8 @@ #![cfg_attr(not(feature = "std"), no_std)] +use core::ops::Mul; +use sp_runtime::Perbill; use { crate::assignment::{Assignment, ChainNumCollators}, frame_support::{pallet_prelude::*, traits::Currency}, @@ -71,6 +73,14 @@ mod mock; #[cfg(test)] mod tests; +#[cfg(test)] +mod tests_with_core_config; + +#[derive(Encode, Decode, Debug)] +pub struct CoreAllocationConfiguration { + pub core_count: u32, + pub max_parachain_percentage: Perbill, +} #[frame_support::pallet] pub mod pallet { @@ -103,6 +113,7 @@ pub mod pallet { type Currency: Currency; type CollatorAssignmentTip: CollatorAssignmentTip>; type ForceEmptyOrchestrator: Get; + type CoreAllocationConfiguration: Get>; /// The weight information of this pallet. type WeightInfo: WeightInfo; } @@ -155,6 +166,90 @@ pub mod pallet { } impl Pallet { + pub(crate) fn order_paras_with_core_config( + mut bulk_paras: Vec, + mut pool_paras: Vec, + core_allocation_configuration: &CoreAllocationConfiguration, + target_session_index: T::SessionIndex, + number_of_collators: u32, + collators_per_container: u32, + collators_per_parathread: u32, + ) -> (Vec, bool) { + let core_count = core_allocation_configuration.core_count; + let ideal_number_of_bulk_paras = core_allocation_configuration + .max_parachain_percentage + .mul(core_count); + + let enough_cores_for_all_paras = bulk_paras.len() < ideal_number_of_bulk_paras as usize; + + // Currently, we are sorting both bulk and pool paras by tip, even when for example + // only number of bulk paras are restricted due to core availability. + // We need to sort both separately as we have fixed space for parachains at the moment + // which means even when we have some parathread cores empty we cannot schedule parachain there. + // We need to change this once other part of algorithm start to differentiate between + // bulk and pool paras. + if !enough_cores_for_all_paras { + bulk_paras.sort_by(|a, b| { + T::CollatorAssignmentTip::get_para_tip(b.para_id) + .cmp(&T::CollatorAssignmentTip::get_para_tip(a.para_id)) + }); + + pool_paras.sort_by(|a, b| { + T::CollatorAssignmentTip::get_para_tip(b.para_id) + .cmp(&T::CollatorAssignmentTip::get_para_tip(a.para_id)) + }); + } + + bulk_paras.truncate(ideal_number_of_bulk_paras as usize); + // We are not truncating pool paras, since their workload is not continuous one core + // can be shared by many parachain during the session. + + let (ordered_chains, sorted_chains_based_on_tip) = Self::order_paras( + bulk_paras, + pool_paras, + target_session_index, + number_of_collators, + collators_per_container, + collators_per_parathread, + ); + + // We should charge tip if either this method or the order_para method has sorted chains based on tip + ( + ordered_chains, + sorted_chains_based_on_tip || !enough_cores_for_all_paras, + ) + } + + pub(crate) fn order_paras( + mut bulk_paras: Vec, + pool_paras: Vec, + target_session_index: T::SessionIndex, + number_of_collators: u32, + collators_per_container: u32, + collators_per_parathread: u32, + ) -> (Vec, bool) { + // Are there enough collators to satisfy the minimum demand? + let enough_collators_for_all_chain = number_of_collators + >= T::HostConfiguration::min_collators_for_orchestrator(target_session_index) + .saturating_add(collators_per_container.saturating_mul(bulk_paras.len() as u32)) + .saturating_add( + collators_per_parathread.saturating_mul(pool_paras.len() as u32), + ); + + let mut chains: Vec<_> = bulk_paras.drain(..).chain(pool_paras).collect(); + + // Prioritize paras by tip on congestion + // As of now this doesn't distinguish between bulk paras and pool paras + if !enough_collators_for_all_chain { + chains.sort_by(|a, b| { + T::CollatorAssignmentTip::get_para_tip(b.para_id) + .cmp(&T::CollatorAssignmentTip::get_para_tip(a.para_id)) + }); + } + + (chains, !enough_collators_for_all_chain) + } + /// Assign new collators /// collators should be queued collators pub fn assign_collators( @@ -162,9 +257,16 @@ pub mod pallet { random_seed: [u8; 32], collators: Vec, ) -> SessionChangeOutcome { + let maybe_core_allocation_configuration = T::CoreAllocationConfiguration::get(); // We work with one session delay to calculate assignments let session_delay = T::SessionIndex::one(); let target_session_index = current_session_index.saturating_add(session_delay); + + let collators_per_container = + T::HostConfiguration::collators_per_container(target_session_index); + let collators_per_parathread = + T::HostConfiguration::collators_per_parathread(target_session_index); + // We get the containerChains that we will have at the target session let container_chains = T::ContainerChains::session_container_chains(target_session_index); @@ -197,7 +299,7 @@ pub mod pallet { let mut shuffle_collators = None; // If the random_seed is all zeros, we don't shuffle the list of collators nor the list // of container chains. - // This should only happen in tests, and in the genesis block. + // This should only happen in tests_without_core_config, and in the genesis block. if random_seed != [0; 32] { let mut rng: ChaCha20Rng = SeedableRng::from_seed(random_seed); container_chain_ids.shuffle(&mut rng); @@ -230,45 +332,45 @@ pub mod pallet { // Chains will not be assigned less than `min_collators`, except the orchestrator chain. // First all chains will be assigned `min_collators`, and then the first one will be assigned up to `max`, // then the second one, and so on. - let mut chains = vec![]; - let collators_per_container = - T::HostConfiguration::collators_per_container(target_session_index); + let mut bulk_paras = vec![]; + let mut pool_paras = vec![]; + for para_id in &container_chain_ids { - chains.push(ChainNumCollators { + bulk_paras.push(ChainNumCollators { para_id: *para_id, min_collators: collators_per_container, max_collators: collators_per_container, }); } - let collators_per_parathread = - T::HostConfiguration::collators_per_parathread(target_session_index); for para_id in ¶threads { - chains.push(ChainNumCollators { + pool_paras.push(ChainNumCollators { para_id: *para_id, min_collators: collators_per_parathread, max_collators: collators_per_parathread, }); } - // Are there enough collators to satisfy the minimum demand? - let enough_collators_for_all_chain = collators.len() as u32 - >= T::HostConfiguration::min_collators_for_orchestrator(target_session_index) - .saturating_add( - collators_per_container.saturating_mul(container_chain_ids.len() as u32), + let (chains, need_to_charge_tip) = + if let Some(core_allocation_configuration) = maybe_core_allocation_configuration { + Self::order_paras_with_core_config( + bulk_paras, + pool_paras, + &core_allocation_configuration, + target_session_index, + collators.len() as u32, + collators_per_container, + collators_per_parathread, ) - .saturating_add( - collators_per_parathread.saturating_mul(parathreads.len() as u32), - ); - - // Prioritize paras by tip on congestion - // As of now this doesn't distinguish between parachains and parathreads - // TODO apply different logic to parathreads - if !enough_collators_for_all_chain { - chains.sort_by(|a, b| { - T::CollatorAssignmentTip::get_para_tip(b.para_id) - .cmp(&T::CollatorAssignmentTip::get_para_tip(a.para_id)) - }); - } + } else { + Self::order_paras( + bulk_paras, + pool_paras, + target_session_index, + collators.len() as u32, + collators_per_container, + collators_per_parathread, + ) + }; // We assign new collators // we use the config scheduled at the target_session_index @@ -330,7 +432,7 @@ pub mod pallet { assigned_containers.retain(|_, v| !v.is_empty()); // On congestion, prioritized chains need to pay the minimum tip of the prioritized chains - let maybe_tip: Option> = if enough_collators_for_all_chain { + let maybe_tip: Option> = if !need_to_charge_tip { None } else { assigned_containers diff --git a/pallets/collator-assignment/src/mock.rs b/pallets/collator-assignment/src/mock.rs index f5f24113a..b401a1f93 100644 --- a/pallets/collator-assignment/src/mock.rs +++ b/pallets/collator-assignment/src/mock.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Tanssi. If not, see +use crate::CoreAllocationConfiguration; +use sp_runtime::Perbill; use { crate::{ self as pallet_collator_assignment, pallet::CollatorContainerChain, @@ -134,6 +136,7 @@ pub struct Mocks { pub container_chains: Vec, pub parathreads: Vec, pub random_seed: [u8; 32], + pub chains_that_are_tipping: Vec, // None means 5 pub full_rotation_period: Option, pub apply_tip: bool, @@ -170,6 +173,11 @@ impl pallet_collator_assignment::GetHostConfiguration for HostConfiguration fn collators_per_parathread(_session_index: u32) -> u32 { MockData::mock().collators_per_parathread } + + fn max_parachain_cores_percentage(_session_index: u32) -> Option { + None + } + #[cfg(feature = "runtime-benchmarks")] fn set_host_configuration(_session_index: u32) { MockData::mutate(|mocks| { @@ -257,7 +265,10 @@ pub struct MockCollatorAssignmentTip; impl CollatorAssignmentTip for MockCollatorAssignmentTip { fn get_para_tip(para_id: ParaId) -> Option { - if MockData::mock().apply_tip && (para_id == 1003u32.into() || para_id == 1004u32.into()) { + if MockData::mock().apply_tip + && ((para_id == 1003u32.into() || para_id == 1004u32.into()) + || MockData::mock().chains_that_are_tipping.contains(¶_id)) + { Some(1_000u32) } else { None @@ -282,6 +293,26 @@ impl CollatorAssignmentHook for MockCollatorAssignmentHook { } } +pub struct GetCoreAllocationConfigurationImpl; + +impl GetCoreAllocationConfigurationImpl { + pub(crate) fn set(config: Option) { + let encoded_data = config.encode(); + sp_io::storage::set(b"___TEST_CONFIG", &encoded_data); + } +} + +impl Get> for GetCoreAllocationConfigurationImpl { + fn get() -> Option { + let maybe_data = sp_io::storage::get(b"___TEST_CONFIG"); + if let Some(data) = maybe_data { + Decode::decode(&mut &data[..]).expect("Data must be able to decode") + } else { + None + } + } +} + impl pallet_collator_assignment::Config for Test { type RuntimeEvent = RuntimeEvent; type SessionIndex = u32; @@ -297,6 +328,7 @@ impl pallet_collator_assignment::Config for Test { type CollatorAssignmentTip = MockCollatorAssignmentTip; type ForceEmptyOrchestrator = ConstBool; type Currency = (); + type CoreAllocationConfiguration = GetCoreAllocationConfigurationImpl; type WeightInfo = (); } diff --git a/pallets/collator-assignment/src/tests_with_core_config.rs b/pallets/collator-assignment/src/tests_with_core_config.rs new file mode 100644 index 000000000..11575631b --- /dev/null +++ b/pallets/collator-assignment/src/tests_with_core_config.rs @@ -0,0 +1,530 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Tanssi is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Tanssi. If not, see + +use crate::assignment::ChainNumCollators; +use crate::mock::*; +use crate::{CoreAllocationConfiguration, Pallet}; +use sp_runtime::Perbill; +use tp_traits::ParaId; + +fn generate_parachains_and_parathreads( + num_parachains: u32, + num_parathreds: u32, +) -> (Vec, Vec) { + let mut parachains = vec![]; + + for i in 0..num_parachains { + parachains.push(ChainNumCollators { + para_id: ParaId::new(i + 1), + min_collators: 0, + max_collators: 0, + }); + } + + let mut parathreads = vec![]; + for i in 0..num_parathreds { + parathreads.push(ChainNumCollators { + para_id: ParaId::new(num_parachains + i + 1), + min_collators: 0, + max_collators: 0, + }); + } + + (parachains, parathreads) +} + +#[test] +fn test_combine_paras_with_core_config() { + let core_count = 10u32; + let num_parachains = 7u32; + let num_parathreads = 5u32; + + let (generated_parachains, generated_parathreads) = + generate_parachains_and_parathreads(num_parachains, num_parathreads); + + // Table for testing + // (config, tipping_chains, expected_resulting_chains, expected_to_be_sorted_by_tip, num_collators) + let table = vec![ + ( + CoreAllocationConfiguration { + core_count, + max_parachain_percentage: Perbill::from_percent(0), + }, + vec![1u32, 2], + vec![ + ChainNumCollators { + para_id: ParaId::new(8), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(9), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(10), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(11), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(12), + min_collators: 0, + max_collators: 0, + }, + ], + true, + 10u32, + ), + ( + CoreAllocationConfiguration { + core_count, + max_parachain_percentage: Perbill::from_percent(1), + }, + vec![5, 8], + vec![ + ChainNumCollators { + para_id: ParaId::new(8), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(9), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(10), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(11), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(12), + min_collators: 0, + max_collators: 0, + }, + ], + true, + 100, + ), + ( + CoreAllocationConfiguration { + core_count, + max_parachain_percentage: Perbill::from_percent(2), + }, + vec![1, 6, 9, 10], + vec![ + ChainNumCollators { + para_id: ParaId::new(9), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(10), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(8), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(11), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(12), + min_collators: 0, + max_collators: 0, + }, + ], + true, + 10, + ), + ( + CoreAllocationConfiguration { + core_count, + max_parachain_percentage: Perbill::from_percent(45), + }, + vec![], + vec![ + ChainNumCollators { + para_id: ParaId::new(1), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(2), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(3), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(4), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(8), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(9), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(10), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(11), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(12), + min_collators: 0, + max_collators: 0, + }, + ], + true, + 100, + ), + ( + CoreAllocationConfiguration { + core_count, + max_parachain_percentage: Perbill::from_percent(50), + }, + vec![4, 6, 7, 8, 12], + vec![ + ChainNumCollators { + para_id: ParaId::new(4), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(6), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(7), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(8), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(12), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(1), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(2), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(9), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(10), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(11), + min_collators: 0, + max_collators: 0, + }, + ], + true, + 10, + ), + ( + CoreAllocationConfiguration { + core_count, + max_parachain_percentage: Perbill::from_percent(98), + }, + vec![1, 12], + vec![ + ChainNumCollators { + para_id: ParaId::new(1), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(12), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(2), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(3), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(4), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(5), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(6), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(7), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(8), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(9), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(10), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(11), + min_collators: 0, + max_collators: 0, + }, + ], + true, + 10, + ), + ( + CoreAllocationConfiguration { + core_count, + max_parachain_percentage: Perbill::from_percent(99), + }, + vec![1, 12], + vec![ + ChainNumCollators { + para_id: ParaId::new(1), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(2), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(3), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(4), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(5), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(6), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(7), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(8), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(9), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(10), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(11), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(12), + min_collators: 0, + max_collators: 0, + }, + ], + false, + 100, + ), + ( + CoreAllocationConfiguration { + core_count, + max_parachain_percentage: Perbill::from_percent(100), + }, + vec![10], + vec![ + ChainNumCollators { + para_id: ParaId::new(10), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(1), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(2), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(3), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(4), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(5), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(6), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(7), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(8), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(9), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(11), + min_collators: 0, + max_collators: 0, + }, + ChainNumCollators { + para_id: ParaId::new(12), + min_collators: 0, + max_collators: 0, + }, + ], + true, + 10, + ), + ]; + + new_test_ext().execute_with(|| { + for ( + config, + mut tipping_chains, + result_chains, + should_be_ordered_by_tip, + number_of_collators, + ) in table + { + MockData::mutate(|mock_data_config| { + mock_data_config.chains_that_are_tipping = + tipping_chains.drain(..).map(Into::into).collect(); + mock_data_config.apply_tip = !mock_data_config.chains_that_are_tipping.is_empty(); + }); + + let (chains, ordered_by_tip) = Pallet::::order_paras_with_core_config( + generated_parachains.clone(), + generated_parathreads.clone(), + &config, + 1, + number_of_collators, + 1, + 1, + ); + + assert_eq!(chains, result_chains); + assert_eq!(ordered_by_tip, should_be_ordered_by_tip); + } + }); +} diff --git a/pallets/configuration/src/lib.rs b/pallets/configuration/src/lib.rs index 3c68e4faa..ab539d050 100644 --- a/pallets/configuration/src/lib.rs +++ b/pallets/configuration/src/lib.rs @@ -86,6 +86,8 @@ pub struct HostConfiguration { pub parathreads_per_collator: u32, /// Ratio of collators that we expect to be assigned to container chains. Affects fees. pub target_container_chain_fullness: Perbill, + /// Maximum number of cores that can be allocated to parachains (only applicable for solo chain) + pub max_parachain_cores_percentage: Option, } impl Default for HostConfiguration { @@ -99,6 +101,7 @@ impl Default for HostConfiguration { collators_per_parathread: 1, parathreads_per_collator: 1, target_container_chain_fullness: Perbill::from_percent(80), + max_parachain_cores_percentage: None, } } } @@ -604,5 +607,18 @@ pub mod pallet { }; config.max_orchestrator_collators } + + fn max_parachain_cores_percentage(session_index: T::SessionIndex) -> Option { + let (past_and_present, _) = Pallet::::pending_configs() + .into_iter() + .partition::, _>(|&(apply_at_session, _)| apply_at_session <= session_index); + + let config = if let Some(last) = past_and_present.last() { + last.1.clone() + } else { + Pallet::::config() + }; + config.max_parachain_cores_percentage + } } } diff --git a/primitives/traits/src/lib.rs b/primitives/traits/src/lib.rs index 05d920f6d..b818744df 100644 --- a/primitives/traits/src/lib.rs +++ b/primitives/traits/src/lib.rs @@ -21,6 +21,7 @@ pub mod alias; +use sp_runtime::Perbill; pub use { alias::*, cumulus_primitives_core::{ @@ -227,6 +228,7 @@ pub trait GetHostConfiguration { fn max_collators_for_orchestrator(session_index: SessionIndex) -> u32; fn collators_per_container(session_index: SessionIndex) -> u32; fn collators_per_parathread(session_index: SessionIndex) -> u32; + fn max_parachain_cores_percentage(session_index: SessionIndex) -> Option; #[cfg(feature = "runtime-benchmarks")] fn set_host_configuration(_session_index: SessionIndex) {} } diff --git a/runtime/common/src/migrations.rs b/runtime/common/src/migrations.rs index 3b5f80fd1..71efdd7ce 100644 --- a/runtime/common/src/migrations.rs +++ b/runtime/common/src/migrations.rs @@ -102,6 +102,7 @@ where collators_per_parathread: default_config.collators_per_parathread, parathreads_per_collator: default_config.parathreads_per_collator, target_container_chain_fullness: default_config.target_container_chain_fullness, + max_parachain_cores_percentage: default_config.max_parachain_cores_percentage, }; frame_support::storage::unhashed::put(CONFIGURATION_ACTIVE_CONFIG_KEY, &new_config); @@ -121,6 +122,7 @@ where collators_per_parathread: default_config.collators_per_parathread, parathreads_per_collator: default_config.parathreads_per_collator, target_container_chain_fullness: default_config.target_container_chain_fullness, + max_parachain_cores_percentage: default_config.max_parachain_cores_percentage, }; new_pending_configs.push((session_index, new_config)); } diff --git a/runtime/dancebox/src/lib.rs b/runtime/dancebox/src/lib.rs index c1ae94a88..6d9b41daf 100644 --- a/runtime/dancebox/src/lib.rs +++ b/runtime/dancebox/src/lib.rs @@ -36,6 +36,7 @@ pub mod weights; #[cfg(test)] mod tests; +use pallet_collator_assignment::CoreAllocationConfiguration; use { cumulus_pallet_parachain_system::{ RelayChainStateProof, RelayNumberMonotonicallyIncreases, RelaychainDataProvider, @@ -887,6 +888,14 @@ impl RemoveParaIdsWithNoCredits for RemoveParaIdsWithNoCreditsImpl { } } +pub struct GetCoreAllocationConfigurationImpl; + +impl Get> for GetCoreAllocationConfigurationImpl { + fn get() -> Option { + None + } +} + impl pallet_collator_assignment::Config for Runtime { type RuntimeEvent = RuntimeEvent; type HostConfiguration = Configuration; @@ -902,6 +911,7 @@ impl pallet_collator_assignment::Config for Runtime { type CollatorAssignmentTip = ServicesPayment; type Currency = Balances; type ForceEmptyOrchestrator = ConstBool; + type CoreAllocationConfiguration = GetCoreAllocationConfigurationImpl; type WeightInfo = weights::pallet_collator_assignment::SubstrateWeight; } diff --git a/runtime/flashbox/src/lib.rs b/runtime/flashbox/src/lib.rs index 3fe027bf3..3f687a3f6 100644 --- a/runtime/flashbox/src/lib.rs +++ b/runtime/flashbox/src/lib.rs @@ -37,6 +37,7 @@ pub mod weights; #[cfg(test)] mod tests; +use pallet_collator_assignment::CoreAllocationConfiguration; use { cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases, cumulus_primitives_core::{relay_chain::SessionIndex, BodyId, ParaId}, @@ -733,6 +734,14 @@ impl ShouldRotateAllCollators for NeverRotateCollators { } } +pub struct GetCoreAllocationConfigurationImpl; + +impl Get> for GetCoreAllocationConfigurationImpl { + fn get() -> Option { + None + } +} + impl pallet_collator_assignment::Config for Runtime { type RuntimeEvent = RuntimeEvent; type HostConfiguration = Configuration; @@ -747,6 +756,7 @@ impl pallet_collator_assignment::Config for Runtime { type CollatorAssignmentTip = ServicesPayment; type Currency = Balances; type ForceEmptyOrchestrator = ConstBool; + type CoreAllocationConfiguration = GetCoreAllocationConfigurationImpl; type WeightInfo = weights::pallet_collator_assignment::SubstrateWeight; } diff --git a/solo-chains/runtime/starlight/src/lib.rs b/solo-chains/runtime/starlight/src/lib.rs index 43c421f2b..af0f9087d 100644 --- a/solo-chains/runtime/starlight/src/lib.rs +++ b/solo-chains/runtime/starlight/src/lib.rs @@ -22,7 +22,7 @@ // Fix compile error in impl_runtime_weights! macro use runtime_common as polkadot_runtime_common; - +use sp_core::Get; use { authority_discovery_primitives::AuthorityId as AuthorityDiscoveryId, beefy_primitives::{ @@ -83,8 +83,8 @@ use { prelude::*, }, tp_traits::{ - apply, derive_storage_traits, GetSessionContainerChains, RemoveParaIdsWithNoCredits, Slot, - SlotFrequency, + apply, derive_storage_traits, GetHostConfiguration, GetSessionContainerChains, + GetSessionIndex, RemoveParaIdsWithNoCredits, Slot, SlotFrequency, }, }; @@ -142,6 +142,7 @@ pub mod xcm_config; // Governance and configurations. pub mod governance; +use pallet_collator_assignment::CoreAllocationConfiguration; use { governance::{ pallet_custom_origins, AuctionAdmin, Fellows, GeneralAdmin, Treasurer, TreasurySpender, @@ -2824,6 +2825,24 @@ impl RemoveParaIdsWithNoCredits for RemoveParaIdsWithNoCreditsImpl { } } +pub struct GetCoreAllocationConfigurationImpl; + +impl Get> for GetCoreAllocationConfigurationImpl { + fn get() -> Option { + let system_config = runtime_parachains::configuration::ActiveConfig::::get(); + + let session_index = CurrentSessionIndexGetter::session_index(); + let max_parachain_percentage = + CollatorConfiguration::max_parachain_cores_percentage(session_index) + .unwrap_or(Perbill::from_percent(50)); + + Some(CoreAllocationConfiguration { + core_count: system_config.scheduler_params.num_cores, + max_parachain_percentage, + }) + } +} + impl pallet_collator_assignment::Config for Runtime { type RuntimeEvent = RuntimeEvent; type HostConfiguration = CollatorConfiguration; @@ -2838,6 +2857,7 @@ impl pallet_collator_assignment::Config for Runtime { type CollatorAssignmentTip = ServicesPayment; type Currency = Balances; type ForceEmptyOrchestrator = ConstBool; + type CoreAllocationConfiguration = GetCoreAllocationConfigurationImpl; type WeightInfo = (); } diff --git a/solo-chains/runtime/starlight/src/tests/collator_assignment_tests.rs b/solo-chains/runtime/starlight/src/tests/collator_assignment_tests.rs index 7a9f92769..77a9d7f69 100644 --- a/solo-chains/runtime/starlight/src/tests/collator_assignment_tests.rs +++ b/solo-chains/runtime/starlight/src/tests/collator_assignment_tests.rs @@ -16,6 +16,7 @@ #![cfg(test)] +use primitives::vstaging::SchedulerParams; use { crate::tests::common::*, crate::{ @@ -1721,7 +1722,16 @@ fn test_collator_assignment_tip_charged_on_congestion() { .with_collators(vec![ (AccountId::from(ALICE), 210 * UNIT), (AccountId::from(BOB), 100 * UNIT), + (AccountId::from(CHARLIE), 210 * UNIT), + (AccountId::from(DAVE), 100 * UNIT), ]) + .with_config(pallet_configuration::HostConfiguration { + collators_per_container: 2, + collators_per_parathread: 1, + min_orchestrator_collators: 0, + max_orchestrator_collators: 0, + ..Default::default() + }) .with_empty_parachains(vec![1001, 1002, 1003]) .build() .execute_with(|| { @@ -1939,3 +1949,492 @@ fn test_collator_assignment_tip_withdraw_min_tip() { ); }); } + +#[test] +fn test_collator_assignment_tip_priority_on_less_cores() { + let parachains = vec![1001u32, 1002u32, 1003u32]; + let parathreads = vec![1004u32, 1005u32, 1006u32, 1007u32]; + + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 210_000 * UNIT), + (AccountId::from(BOB), 100_000 * UNIT), + (AccountId::from(CHARLIE), 100_000 * UNIT), + (AccountId::from(DAVE), 100_000 * UNIT), + ]) + .with_collators(vec![ + (AccountId::from(ALICE), 210 * UNIT), + (AccountId::from(BOB), 100 * UNIT), + (AccountId::from(CHARLIE), 100 * UNIT), + (AccountId::from(DAVE), 100 * UNIT), + (AccountId::from(EVE), 100 * UNIT), + (AccountId::from(FERDIE), 100 * UNIT), + ]) + .with_empty_parachains(parachains.clone()) + .with_additional_empty_parathreads(parathreads.clone()) + .with_relay_config(runtime_parachains::configuration::HostConfiguration { + scheduler_params: SchedulerParams { + num_cores: 4, + ..Default::default() + }, + ..Default::default() + }) + .with_config(pallet_configuration::HostConfiguration { + collators_per_container: 2, + collators_per_parathread: 1, + ..Default::default() + }) + .build() + .execute_with(|| { + // Parachains and parathreads are sorted separately + let parachain_id_offering_tip: ParaId = 1003u32.into(); + let parachain_ids_without_tip: Vec = parachains + .iter() + .filter_map(|parachain| { + if ParaId::new(*parachain) == parachain_id_offering_tip { + None + } else { + Some((*parachain).into()) + } + }) + .collect(); + let parathread_ids_offering_tip: Vec = vec![1005u32.into(), 1006u32.into()]; + let parathread_ids_without_tip: Vec = parathreads + .iter() + .filter_map(|parathread| { + if parathread_ids_offering_tip.contains(&((*parathread).into())) { + None + } else { + Some((*parathread).into()) + } + }) + .collect(); + let tank_funds = 100 * UNIT; + let max_tip_for_parachain = 1 * UNIT; + let max_tip_for_parathread = 10 * UNIT; + + // 1003 should not be part of the container chains as we have less cores available + assert_eq!( + TanssiCollatorAssignment::collator_container_chain() + .container_chains + .keys() + .cloned() + .collect::>(), + vec![ + 1001u32.into(), + 1002u32.into(), + 1004u32.into(), + 1005u32.into(), + 1006u32.into(), + 1007u32.into() + ] + ); + + // Send funds to tank + assert_ok!(ServicesPayment::purchase_credits( + origin_of(ALICE.into()), + parachain_id_offering_tip, + tank_funds, + )); + + // Set tip for 1003 + assert_ok!(ServicesPayment::set_max_tip( + root_origin(), + parachain_id_offering_tip, + Some(max_tip_for_parachain), + )); + + for parathread_id in ¶thread_ids_offering_tip { + assert_ok!(ServicesPayment::purchase_credits( + origin_of(ALICE.into()), + *parathread_id, + tank_funds, + )); + + assert_ok!(ServicesPayment::set_max_tip( + root_origin(), + *parathread_id, + Some(max_tip_for_parathread), + )); + } + + run_to_session(2); + + assert_eq!( + TanssiCollatorAssignment::collator_container_chain().container_chains + [¶chain_id_offering_tip] + .len(), + 2, + ); + + // The first parachain has collator even without tip as it is highest priority without tip + assert_eq!( + TanssiCollatorAssignment::collator_container_chain().container_chains + [¶chain_ids_without_tip + .first() + .expect("at least one parachain id is without tip")] + .len(), + 2 + ); + + for parachain_id in &mut parachain_ids_without_tip.iter().skip(1) { + assert_eq!( + TanssiCollatorAssignment::collator_container_chain() + .container_chains + .get(parachain_id), + None + ); + } + + for parathread_id in ¶thread_ids_offering_tip { + assert_eq!( + TanssiCollatorAssignment::collator_container_chain().container_chains + [¶thread_id] + .len(), + 1, + ); + } + + for parathread_id in ¶thread_ids_without_tip { + assert_eq!( + TanssiCollatorAssignment::collator_container_chain().container_chains + [¶thread_id] + .len(), + 0 + ); + } + + // Now 1003 is part of container chains with collator as we sorted by tip + // And 1005 and 1006 as well for parathread + // Even though parathread's tip is 10 times more it cannot kick out parachain + assert_eq!( + TanssiCollatorAssignment::collator_container_chain() + .container_chains + .keys() + .cloned() + .collect::>(), + vec![ + 1001u32.into(), + 1003u32.into(), + 1004u32.into(), + 1005u32.into(), + 1006u32.into(), + 1007u32.into() + ] + ); + }); + + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 210_000 * UNIT), + (AccountId::from(BOB), 100_000 * UNIT), + (AccountId::from(CHARLIE), 100_000 * UNIT), + (AccountId::from(DAVE), 100_000 * UNIT), + ]) + .with_collators(vec![ + (AccountId::from(ALICE), 210 * UNIT), + (AccountId::from(BOB), 100 * UNIT), + (AccountId::from(CHARLIE), 100 * UNIT), + (AccountId::from(DAVE), 100 * UNIT), + (AccountId::from(EVE), 100 * UNIT), + (AccountId::from(FERDIE), 100 * UNIT), + ]) + .with_empty_parachains(parachains.clone()) + .with_additional_empty_parathreads(parathreads.clone()) + .with_relay_config(runtime_parachains::configuration::HostConfiguration { + scheduler_params: SchedulerParams { + num_cores: 4, + ..Default::default() + }, + ..Default::default() + }) + .with_config(pallet_configuration::HostConfiguration { + collators_per_container: 2, + collators_per_parathread: 1, + ..Default::default() + }) + .build() + .execute_with(|| { + // Parachains and parathreads are sorted separately + let parachain_ids_offering_tip: Vec = vec![1002u32.into(), 1003u32.into()]; + let parachain_ids_without_tip: Vec = parachains + .iter() + .filter_map(|parachain| { + if parachain_ids_offering_tip.contains(&((*parachain).into())) { + None + } else { + Some((*parachain).into()) + } + }) + .collect(); + let parathread_id_offering_tip: ParaId = 1006u32.into(); + let parathread_ids_without_tip: Vec = parathreads + .iter() + .filter_map(|parathread| { + if ParaId::new(*parathread) == parathread_id_offering_tip { + None + } else { + Some((*parathread).into()) + } + }) + .collect(); + let tank_funds = 100 * UNIT; + let max_tip_for_parachain = 10 * UNIT; + let max_tip_for_parathread = 1 * UNIT; + + // 1003 should not be part of the container chains as we have less cores available + assert_eq!( + TanssiCollatorAssignment::collator_container_chain() + .container_chains + .keys() + .cloned() + .collect::>(), + vec![ + 1001u32.into(), + 1002u32.into(), + 1004u32.into(), + 1005u32.into(), + 1006u32.into(), + 1007u32.into() + ] + ); + + for parachain_id in ¶chain_ids_offering_tip { + // Send funds to tank + assert_ok!(ServicesPayment::purchase_credits( + origin_of(ALICE.into()), + *parachain_id, + tank_funds, + )); + + assert_ok!(ServicesPayment::set_max_tip( + root_origin(), + *parachain_id, + Some(max_tip_for_parachain), + )); + } + + assert_ok!(ServicesPayment::purchase_credits( + origin_of(ALICE.into()), + parathread_id_offering_tip, + tank_funds, + )); + + assert_ok!(ServicesPayment::set_max_tip( + root_origin(), + parathread_id_offering_tip, + Some(max_tip_for_parathread), + )); + + run_to_session(2); + + for parachain_id in ¶chain_ids_offering_tip { + assert_eq!( + TanssiCollatorAssignment::collator_container_chain().container_chains + [parachain_id] + .len(), + 2, + ); + } + + // No parachain without tip has any collators as all cores dedicated to parachains are filled + // by tipping parachains. + for parachain_id in ¶chain_ids_without_tip { + assert_eq!( + TanssiCollatorAssignment::collator_container_chain() + .container_chains + .get(parachain_id), + None + ); + } + + assert_eq!( + TanssiCollatorAssignment::collator_container_chain().container_chains + [¶thread_id_offering_tip] + .len(), + 1, + ); + + // The first parathread has collator even without tip as it is highest priority without tip and we have one collator remaining + assert_eq!( + TanssiCollatorAssignment::collator_container_chain().container_chains + [¶thread_ids_without_tip + .first() + .expect("at least one parathread id is without tip")] + .len(), + 1 + ); + + for parathread_id in &mut parathread_ids_without_tip.iter().skip(1) { + assert_eq!( + TanssiCollatorAssignment::collator_container_chain().container_chains + [¶thread_id] + .len(), + 0 + ); + } + + // Now 1003 is part of container chains with collator as we sorted by tip + // And 1005 and 1006 as well for parathread + // Even though parachain's tip is 10 times more it cannot kick out parathread + assert_eq!( + TanssiCollatorAssignment::collator_container_chain() + .container_chains + .keys() + .cloned() + .collect::>(), + vec![ + 1002u32.into(), + 1003u32.into(), + 1004u32.into(), + 1005u32.into(), + 1006u32.into(), + 1007u32.into() + ] + ); + }); +} + +#[test] +fn test_collator_assignment_parathreads_adjusted_on_vacant_parachain_core() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 210_000 * UNIT), + (AccountId::from(BOB), 100_000 * UNIT), + (AccountId::from(CHARLIE), 100_000 * UNIT), + (AccountId::from(DAVE), 100_000 * UNIT), + ]) + .with_collators(vec![ + (AccountId::from(ALICE), 210 * UNIT), + (AccountId::from(BOB), 100 * UNIT), + (AccountId::from(CHARLIE), 100 * UNIT), + (AccountId::from(DAVE), 100 * UNIT), + ]) + .with_empty_parachains(vec![1001, 1002]) + .with_additional_empty_parathreads(vec![1003, 1004, 1005, 1006]) + .with_relay_config(runtime_parachains::configuration::HostConfiguration { + scheduler_params: SchedulerParams { + num_cores: 6, + ..Default::default() + }, + ..Default::default() + }) + .build() + .execute_with(|| { + let parathread_id: ParaId = 1006u32.into(); + let max_tip_for_parathread = 1 * UNIT; + let tank_funds = 100 * UNIT; + + assert_ok!(ServicesPayment::purchase_credits( + origin_of(ALICE.into()), + parathread_id, + tank_funds, + )); + + assert_ok!(ServicesPayment::set_max_tip( + root_origin(), + parathread_id, + Some(max_tip_for_parathread), + )); + + run_to_session(2); + + // Even though parachains can only get 50% of cores since we have vacant parachain core, it can be allocated to parathreads + // and we are not considering tips + assert_eq!( + TanssiCollatorAssignment::collator_container_chain() + .container_chains + .keys() + .cloned() + .collect::>(), + vec![ + 1001u32.into(), + 1002u32.into(), + 1003u32.into(), + 1004u32.into(), + 1005u32.into(), + 1006u32.into() + ] + ); + }); +} + +#[test] +fn test_collator_assignment_parachain_cannot_be_adjusted_on_vacant_parathread_core() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 210_000 * UNIT), + (AccountId::from(BOB), 100_000 * UNIT), + (AccountId::from(CHARLIE), 100_000 * UNIT), + (AccountId::from(DAVE), 100_000 * UNIT), + ]) + .with_collators(vec![ + (AccountId::from(ALICE), 210 * UNIT), + (AccountId::from(BOB), 100 * UNIT), + (AccountId::from(CHARLIE), 100 * UNIT), + (AccountId::from(DAVE), 100 * UNIT), + ]) + .with_empty_parachains(vec![1001, 1002, 1003, 1004, 1005]) + .with_additional_empty_parathreads(vec![1006]) + .with_relay_config(runtime_parachains::configuration::HostConfiguration { + scheduler_params: SchedulerParams { + num_cores: 6, + ..Default::default() + }, + ..Default::default() + }) + .build() + .execute_with(|| { + // Parachains and parathreads are sorted separately + let parachain_id: ParaId = 1005u32.into(); + let tank_funds = 100 * UNIT; + let max_tip_for_parachain = 1 * UNIT; + + // 1003 should not be part of the container chains as we have less cores available + assert_eq!( + TanssiCollatorAssignment::collator_container_chain() + .container_chains + .keys() + .cloned() + .collect::>(), + vec![ + 1001u32.into(), + 1002u32.into(), + 1003u32.into(), + 1006u32.into() + ] + ); + + // Send funds to tank + assert_ok!(ServicesPayment::purchase_credits( + origin_of(ALICE.into()), + parachain_id, + tank_funds, + )); + + // Set tip for 1003 + assert_ok!(ServicesPayment::set_max_tip( + root_origin(), + parachain_id, + Some(max_tip_for_parachain), + )); + + run_to_session(2); + + // Even when we have vacant parathread core, it cannot be allocated to parachain + // tips can be used to get the scarce parachain core + assert_eq!( + TanssiCollatorAssignment::collator_container_chain() + .container_chains + .keys() + .cloned() + .collect::>(), + vec![ + 1001u32.into(), + 1002u32.into(), + 1005u32.into(), + 1006u32.into() + ] + ); + }); +} diff --git a/solo-chains/runtime/starlight/src/tests/common/mod.rs b/solo-chains/runtime/starlight/src/tests/common/mod.rs index ebf3593c7..9b3df1251 100644 --- a/solo-chains/runtime/starlight/src/tests/common/mod.rs +++ b/solo-chains/runtime/starlight/src/tests/common/mod.rs @@ -285,7 +285,13 @@ impl Default for ExtBuilder { sudo: Default::default(), para_ids: Default::default(), config: default_config(), - relay_config: Default::default(), + relay_config: runtime_parachains::configuration::HostConfiguration { + scheduler_params: SchedulerParams { + num_cores: 6, + ..Default::default() + }, + ..Default::default() + }, own_para_id: Default::default(), next_free_para_id: Default::default(), keystore: None, @@ -335,6 +341,24 @@ impl ExtBuilder { self } + pub fn with_additional_empty_parathreads(mut self, para_ids: Vec) -> Self { + self.para_ids = self + .para_ids + .iter() + .cloned() + .chain(para_ids.into_iter().map(|para_id| ParaRegistrationParams { + para_id, + genesis_data: empty_genesis_data(), + block_production_credits: u32::MAX, + collator_assignment_credits: u32::MAX, + parathread_params: Some(ParathreadParams { + slot_frequency: Default::default(), + }), + })) + .collect(); + self + } + // Maybe change to with_collators_config? pub fn with_config(mut self, config: pallet_configuration::HostConfiguration) -> Self { self.config = config; @@ -1043,6 +1067,9 @@ impl ParasInherentTestBuilder } use frame_support::StorageHasher; +use primitives::vstaging::SchedulerParams; +use tp_traits::ParathreadParams; + pub fn storage_map_final_key( pallet_prefix: &str, map_name: &str,