--- a/node/cli/src/chain_spec.rs +++ b/node/cli/src/chain_spec.rs @@ -24,7 +24,6 @@ use serde_json::map::Map; use up_common::types::opaque::*; -use up_common::constants::{GENESIS_LICENSE_BOND, SESSION_LENGTH}; #[cfg(feature = "unique-runtime")] pub use unique_runtime as default_runtime; @@ -196,9 +195,6 @@ .cloned() .map(|(acc, _)| acc) .collect(), - desired_collators: 10, - license_bond: GENESIS_LICENSE_BOND, - kick_threshold: SESSION_LENGTH, }, session: SessionConfig { keys: $initial_invulnerables --- a/node/cli/src/service.rs +++ b/node/cli/src/service.rs @@ -921,7 +921,7 @@ .import_notification_stream() .map(|_| EngineCommand::SealNewBlock { create_empty: true, - finalize: false, + finalize: false, // todo:collator finalize true parent_hash: None, sender: None, }), @@ -932,7 +932,7 @@ dyn Stream> + Send + Sync + Unpin, > = Box::new(autoseal_interval.map(|_| EngineCommand::SealNewBlock { create_empty: true, - finalize: false, + finalize: false, // todo:collator finalize true parent_hash: None, sender: None, })); --- a/pallets/collator-selection/Cargo.toml +++ b/pallets/collator-selection/Cargo.toml @@ -25,6 +25,7 @@ frame-system = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.33" } pallet-authorship = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.33" } pallet-session = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.33" } +pallet-configuration = { default-features = false, path = "../configuration" } frame-benchmarking = { default-features = false, optional = true, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.33" } --- a/pallets/collator-selection/src/lib.rs +++ b/pallets/collator-selection/src/lib.rs @@ -105,16 +105,15 @@ }, BoundedVec, PalletId, }; - use frame_system::{pallet_prelude::*, Config as SystemConfig}; + use frame_system::pallet_prelude::*; use pallet_session::SessionManager; - use sp_runtime::{ - Perbill, - traits::{One, Convert}, + use sp_runtime::{Perbill, traits::Convert}; + use pallet_configuration::{ + CollatorSelectionDesiredCollatorsOverride as DesiredCollators, + CollatorSelectionLicenseBondOverride as LicenseBond, + CollatorSelectionKickThresholdOverride as KickThreshold, BalanceOf, }; use sp_staking::SessionIndex; - - type BalanceOf = - <::Currency as Currency<::AccountId>>::Balance; /// A convertor from collators id. Since this pallet does not have stash/controller, this is /// just identity. @@ -127,13 +126,10 @@ /// Configure the pallet by specifying the parameters and types on which it depends. #[pallet::config] - pub trait Config: frame_system::Config { + pub trait Config: frame_system::Config + pallet_configuration::Config { /// Overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// The currency mechanism. - type Currency: ReservableCurrency; - /// Origin that can dictate updating parameters of this pallet. type UpdateOrigin: EnsureOrigin; @@ -176,8 +172,8 @@ /// The (community) collation license holders. #[pallet::storage] - #[pallet::getter(fn licenses)] - pub type Licenses = + #[pallet::getter(fn license_deposit_of)] + pub type LicenseDepositOf = StorageMap<_, Blake2_128Concat, T::AccountId, BalanceOf, ValueQuery>; /// The (community, limited) collation candidates. @@ -185,40 +181,16 @@ #[pallet::getter(fn candidates)] pub type Candidates = StorageValue<_, BoundedVec, ValueQuery>; - - /// Collator will be kicked if it does not produce a block within the threshold (does not apply to invulnerables). - /// - /// Should be a multiple of session or things will get inconsistent. todo:collator reword? - #[pallet::storage] - #[pallet::getter(fn kick_threshold)] - pub type KickThreshold = StorageValue<_, T::BlockNumber, ValueQuery>; /// Last block authored by collator. #[pallet::storage] #[pallet::getter(fn last_authored_block)] pub type LastAuthoredBlock = StorageMap<_, Twox64Concat, T::AccountId, T::BlockNumber, ValueQuery>; - - /// Desired number of candidates. - /// - /// This should ideally always be less than [`Config::MaxCollators`] for weights to be correct. - #[pallet::storage] - #[pallet::getter(fn desired_collators)] - pub type DesiredCollators = StorageValue<_, u32, ValueQuery>; - /// Fixed amount to deposit to become a collator. - /// - /// When a collator calls `leave_intent` they immediately receive the deposit back. - #[pallet::storage] - #[pallet::getter(fn license_bond)] - pub type LicenseBond = StorageValue<_, BalanceOf, ValueQuery>; - #[pallet::genesis_config] pub struct GenesisConfig { pub invulnerables: Vec, - pub license_bond: BalanceOf, - pub kick_threshold: T::BlockNumber, - pub desired_collators: u32, } #[cfg(feature = "std")] @@ -226,9 +198,6 @@ fn default() -> Self { Self { invulnerables: Default::default(), - license_bond: Default::default(), - kick_threshold: T::BlockNumber::one(), - desired_collators: Default::default(), } } } @@ -248,14 +217,7 @@ let bounded_invulnerables = BoundedVec::<_, T::MaxCollators>::try_from(self.invulnerables.clone()) .expect("genesis invulnerables are more than T::MaxCollators"); - assert!( - T::MaxCollators::get() >= self.desired_collators, - "genesis desired_collators are more than T::MaxCollators", - ); - - >::put(self.desired_collators); - >::put(self.license_bond); - >::put(self.kick_threshold); + >::put(bounded_invulnerables); } } @@ -263,15 +225,6 @@ #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - NewDesiredCollators { - desired_collators: u32, - }, - NewLicenseBond { - bond_amount: BalanceOf, - }, - NewKickThreshold { - length_in_blocks: T::BlockNumber, - }, InvulnerableAdded { invulnerable: T::AccountId, }, @@ -383,51 +336,6 @@ Ok(().into()) } - /// Set the ideal number of collators. If lowering this number, - /// then the number of running collators could be higher than this figure. - /// Aside from that edge case, there should be no other way to have more collators than the desired number. - #[pallet::weight(T::WeightInfo::set_desired_collators())] - pub fn set_desired_collators(origin: OriginFor, max: u32) -> DispatchResultWithPostInfo { - T::UpdateOrigin::ensure_origin(origin)?; - // we trust origin calls, this is just a for more accurate benchmarking - if max > T::MaxCollators::get() { - log::warn!("max > T::MaxCollators; you might need to run benchmarks again"); - } - >::put(max); - Self::deposit_event(Event::NewDesiredCollators { - desired_collators: max, - }); - Ok(().into()) - } - - /// Set the candidacy bond amount. - #[pallet::weight(T::WeightInfo::set_license_bond())] - pub fn set_license_bond( - origin: OriginFor, - bond: BalanceOf, - ) -> DispatchResultWithPostInfo { - T::UpdateOrigin::ensure_origin(origin)?; - >::put(bond); - Self::deposit_event(Event::NewLicenseBond { bond_amount: bond }); - Ok(().into()) - } - - /// Set the length of the kick threshold. - /// Note that if the length is not a multiple of the session period, it might get inconsistent. - #[pallet::weight(T::WeightInfo::set_license_bond())] // todo:collator weight - pub fn set_kick_threshold( - origin: OriginFor, - kick_threshold: T::BlockNumber, - ) -> DispatchResultWithPostInfo { - T::UpdateOrigin::ensure_origin(origin)?; - // todo:collator insert something to guarantee consistency? - >::put(kick_threshold); - Self::deposit_event(Event::NewKickThreshold { - length_in_blocks: kick_threshold, - }); - Ok(().into()) - } - /// Purchase a license on block collation for this account. /// It does not make it a collator candidate, use `onboard` afterward. The account must /// (a) already have registered session keys and (b) be able to reserve the `LicenseBond`. @@ -438,7 +346,7 @@ // register_as_candidate let who = ensure_signed(origin)?; - if Licenses::::contains_key(&who) { + if LicenseDepositOf::::contains_key(&who) { return Err(Error::::AlreadyHoldingLicense.into()); } @@ -449,10 +357,10 @@ Error::::ValidatorNotRegistered ); - let deposit = Self::license_bond(); + let deposit = >::get(); T::Currency::reserve(&who, deposit)?; - Licenses::::insert(who.clone(), deposit); + LicenseDepositOf::::insert(who.clone(), deposit); Self::deposit_event(Event::LicenseObtained { account_id: who, @@ -471,12 +379,15 @@ let who = ensure_signed(origin)?; // ensure the user obtained the license. - ensure!(Licenses::::contains_key(&who), Error::::NoLicense); + ensure!( + LicenseDepositOf::::contains_key(&who), + Error::::NoLicense + ); // ensure we are below limit. let length = >::decode_len().unwrap_or_default() + >::decode_len().unwrap_or_default(); ensure!( - (length as u32) < Self::desired_collators(), + (length as u32) < >::get(), Error::::TooManyCandidates ); ensure!( @@ -495,7 +406,7 @@ // First authored block is current block plus kick threshold to handle session delay >::insert( who.clone(), - frame_system::Pallet::::block_number() + Self::kick_threshold(), + frame_system::Pallet::::block_number() + >::get(), ); Ok(candidates.len()) } @@ -594,7 +505,7 @@ /// Removes a candidate if they exist and sends them back their deposit, optionally slashed. fn try_release_license(who: &T::AccountId, should_slash: bool) -> DispatchResult { let mut deposit_returned = BalanceOf::::default(); - Licenses::::try_mutate_exists(who, |deposit| -> DispatchResult { + LicenseDepositOf::::try_mutate_exists(who, |deposit| -> DispatchResult { if let Some(deposit) = deposit.take() { if should_slash { let slashed = T::SlashRatio::get() * deposit; @@ -640,7 +551,7 @@ candidates: BoundedVec, ) -> BoundedVec { let now = frame_system::Pallet::::block_number(); - let kick_threshold = Self::kick_threshold(); + let kick_threshold = >::get(); candidates .into_iter() .filter_map(|c| { --- a/pallets/collator-selection/src/mock.rs +++ b/pallets/collator-selection/src/mock.rs @@ -63,6 +63,7 @@ Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, CollatorSelection: collator_selection::{Pallet, Call, Storage, Event}, Authorship: pallet_authorship::{Pallet, Call, Storage, Inherent}, + Configuration: pallet_configuration::{Pallet, Call, Storage, Event}, } ); @@ -200,13 +201,38 @@ type WeightInfo = (); } +parameter_types! { + pub const MaxCollators: u32 = 5; + pub const LicenseBond: u64 = 10; + pub const KickThreshold: u64 = 10; + // the following values do not matter and are meaningless, etc. + pub const DefaultWeightToFeeCoefficient: u32 = 100_000; + pub const DefaultMinGasPrice: u64 = 100_000; + pub const MaxXcmAllowedLocations: u32 = 16; + pub AppPromotionDailyRate: Perbill = Perbill::from_rational(5u32, 10_000); + pub const DayRelayBlocks: u32 = 1; +} + +impl pallet_configuration::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type DefaultCollatorSelectionMaxCollators = MaxCollators; + type DefaultCollatorSelectionKickThreshold = KickThreshold; + type DefaultCollatorSelectionLicenseBond = LicenseBond; + // the following we don't care about + type DefaultWeightToFeeCoefficient = DefaultWeightToFeeCoefficient; + type DefaultMinGasPrice = DefaultMinGasPrice; + type MaxXcmAllowedLocations = MaxXcmAllowedLocations; + type AppPromotionDailyRate = AppPromotionDailyRate; + type DayRelayBlocks = DayRelayBlocks; +} + ord_parameter_types! { pub const RootAccount: u64 = 777; } parameter_types! { pub const PotId: PalletId = PalletId(*b"PotStake"); - pub const MaxCollators: u32 = 20; pub const MaxAuthorities: u32 = 100_000; pub const SlashRatio: Perbill = Perbill::one(); } @@ -224,7 +250,6 @@ impl Config for Test { type RuntimeEvent = RuntimeEvent; - type Currency = Balances; type UpdateOrigin = EnsureSignedBy; type PotId = PotId; type MaxCollators = MaxCollators; @@ -257,9 +282,6 @@ }) .collect::>(); let collator_selection = collator_selection::GenesisConfig:: { - desired_collators: 5, - license_bond: 10, - kick_threshold: 10, invulnerables, }; let session = pallet_session::GenesisConfig:: { keys }; --- a/pallets/collator-selection/src/tests.rs +++ b/pallets/collator-selection/src/tests.rs @@ -36,8 +36,14 @@ assert_noop, assert_ok, traits::{Currency, GenesisBuild, OnInitialize}, }; +use frame_system::RawOrigin; use pallet_balances::Error as BalancesError; use sp_runtime::traits::BadOrigin; +use pallet_configuration::{ + CollatorSelectionDesiredCollatorsOverride as DesiredCollators, + CollatorSelectionKickThresholdOverride as KickThreshold, + CollatorSelectionLicenseBondOverride as LicenseBond, +}; fn get_license_and_onboard(account_id: ::AccountId) { assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed( @@ -51,8 +57,8 @@ #[test] fn basic_setup_works() { new_test_ext().execute_with(|| { - assert_eq!(CollatorSelection::desired_collators(), 5); - assert_eq!(CollatorSelection::license_bond(), 10); + assert_eq!(>::get(), 5); + assert_eq!(>::get(), 10); assert!(CollatorSelection::candidates().is_empty()); assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]); @@ -122,18 +128,21 @@ fn set_desired_collators_works() { new_test_ext().execute_with(|| { // given - assert_eq!(CollatorSelection::desired_collators(), 5); + assert_eq!(>::get(), 5); // can set - assert_ok!(CollatorSelection::set_desired_collators( - RuntimeOrigin::signed(RootAccount::get()), - 7 + assert_ok!(Configuration::set_collator_selection_desired_collators( + RawOrigin::Root.into(), + Some(7) )); - assert_eq!(CollatorSelection::desired_collators(), 7); + assert_eq!(>::get(), 7); // rejects bad origin assert_noop!( - CollatorSelection::set_desired_collators(RuntimeOrigin::signed(1), 8), + Configuration::set_collator_selection_desired_collators( + RuntimeOrigin::signed(1), + Some(8) + ), BadOrigin ); }); @@ -143,18 +152,18 @@ fn set_license_bond() { new_test_ext().execute_with(|| { // given - assert_eq!(CollatorSelection::license_bond(), 10); + assert_eq!(>::get(), 10); // can set - assert_ok!(CollatorSelection::set_license_bond( - RuntimeOrigin::signed(RootAccount::get()), - 7 + assert_ok!(Configuration::set_collator_selection_license_bond( + RawOrigin::Root.into(), + Some(7) )); - assert_eq!(CollatorSelection::license_bond(), 7); + assert_eq!(>::get(), 7); // rejects bad origin. assert_noop!( - CollatorSelection::set_license_bond(RuntimeOrigin::signed(1), 8), + Configuration::set_collator_selection_license_bond(RuntimeOrigin::signed(1), Some(8)), BadOrigin ); }); @@ -179,7 +188,7 @@ fn cannot_onboard_candidate_if_too_many() { new_test_ext().execute_with(|| { // reset desired candidates - >::put(0); + >::put(0); // can still get a license. assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(4))); @@ -191,7 +200,7 @@ ); // reset desired candidates to invulnerables + 1 - >::put(3); + >::put(3); assert_ok!(CollatorSelection::onboard(RuntimeOrigin::signed(4))); // but no more. @@ -236,7 +245,7 @@ new_test_ext().execute_with(|| { // can add 3 as candidate get_license_and_onboard(3); - assert_eq!(CollatorSelection::licenses(3), 10); + assert_eq!(CollatorSelection::license_deposit_of(3), 10); assert_eq!(CollatorSelection::candidates(), vec![3]); assert_eq!(CollatorSelection::last_authored_block(3), 10); assert_eq!(Balances::free_balance(3), 90); @@ -257,8 +266,8 @@ fn becoming_candidate_works() { new_test_ext().execute_with(|| { // given - assert_eq!(CollatorSelection::desired_collators(), 5); - assert_eq!(CollatorSelection::license_bond(), 10); + assert_eq!(>::get(), 5); + assert_eq!(>::get(), 10); assert_eq!(CollatorSelection::candidates(), Vec::new()); assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]); @@ -315,7 +324,7 @@ )); // should exclude from candidates, but not revoke the license assert_eq!(CollatorSelection::candidates(), vec![]); - assert_eq!(CollatorSelection::licenses(3), 10); + assert_eq!(CollatorSelection::license_deposit_of(3), 10); assert_eq!(Balances::free_balance(3), 90); }); } @@ -503,7 +512,7 @@ assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 3, 4]); assert_eq!(CollatorSelection::candidates(), vec![4]); - assert_eq!(CollatorSelection::kick_threshold(), 10); + assert_eq!(>::get(), 10); assert_eq!(CollatorSelection::last_authored_block(4), 20); initialize_to_block(30); @@ -524,9 +533,6 @@ let invulnerables = vec![1, 1]; let collator_selection = collator_selection::GenesisConfig:: { - desired_collators: 5, - license_bond: 10, - kick_threshold: 10, invulnerables, }; // collator selection must be initialized before session. --- a/pallets/configuration/src/lib.rs +++ b/pallets/configuration/src/lib.rs @@ -36,15 +36,24 @@ mod pallet { use super::*; use frame_support::{ - traits::Get, - pallet_prelude::{StorageValue, ValueQuery, DispatchResult, OptionQuery}, - BoundedVec, + traits::{Get, ReservableCurrency, Currency}, + pallet_prelude::{StorageValue, ValueQuery, DispatchResult, IsType, OptionQuery}, + BoundedVec, log, }; - use frame_system::{pallet_prelude::OriginFor, ensure_root}; + use frame_system::{pallet_prelude::OriginFor, ensure_root, Config as SystemConfig}; use xcm::v1::MultiLocation; + pub type BalanceOf = + <::Currency as Currency<::AccountId>>::Balance; + #[pallet::config] pub trait Config: frame_system::Config { + /// Overarching event type. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + + /// The currency mechanism. + type Currency: ReservableCurrency; + #[pallet::constant] type DefaultWeightToFeeCoefficient: Get; @@ -57,6 +66,27 @@ type AppPromotionDailyRate: Get; #[pallet::constant] type DayRelayBlocks: Get; + + #[pallet::constant] + type DefaultCollatorSelectionMaxCollators: Get; + #[pallet::constant] + type DefaultCollatorSelectionLicenseBond: Get>; + #[pallet::constant] + type DefaultCollatorSelectionKickThreshold: Get; + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + NewDesiredCollators { + desired_collators: Option, + }, + NewCollatorLicenseBond { + bond_cost: Option>, + }, + NewCollatorKickThreshold { + length_in_blocks: Option, + }, } #[pallet::error] @@ -85,6 +115,27 @@ pub type AppPromomotionConfigurationOverride = StorageValue, QueryKind = ValueQuery>; + #[pallet::storage] + pub type CollatorSelectionDesiredCollatorsOverride = StorageValue< + Value = u32, + QueryKind = ValueQuery, + OnEmpty = T::DefaultCollatorSelectionMaxCollators, + >; + + #[pallet::storage] + pub type CollatorSelectionLicenseBondOverride = StorageValue< + Value = BalanceOf, + QueryKind = ValueQuery, + OnEmpty = T::DefaultCollatorSelectionLicenseBond, + >; + + #[pallet::storage] + pub type CollatorSelectionKickThresholdOverride = StorageValue< + Value = T::BlockNumber, + QueryKind = ValueQuery, + OnEmpty = T::DefaultCollatorSelectionKickThreshold, + >; + #[pallet::call] impl Pallet { #[pallet::weight(T::DbWeight::get().writes(1))] @@ -144,6 +195,55 @@ Ok(()) } + + #[pallet::weight(T::DbWeight::get().writes(1))] + pub fn set_collator_selection_desired_collators( + origin: OriginFor, + max: Option, + ) -> DispatchResult { + ensure_root(origin)?; + if let Some(max) = max { + // we trust origin calls, this is just a for more accurate benchmarking + if max > T::DefaultCollatorSelectionMaxCollators::get() { + log::warn!("max > T::DefaultCollatorSelectionMaxCollators; you might need to run benchmarks again"); + } + >::set(max); + } else { + >::kill(); + } + Self::deposit_event(Event::NewDesiredCollators { desired_collators: max }); + Ok(()) + } + + #[pallet::weight(T::DbWeight::get().writes(1))] + pub fn set_collator_selection_license_bond( + origin: OriginFor, + amount: Option>, + ) -> DispatchResult { + ensure_root(origin)?; + if let Some(amount) = amount { + >::set(amount); + } else { + >::kill(); + } + Self::deposit_event(Event::NewCollatorLicenseBond { bond_cost: amount }); + Ok(()) + } + + #[pallet::weight(T::DbWeight::get().writes(1))] + pub fn set_collator_selection_kick_threshold( + origin: OriginFor, + threshold: Option, + ) -> DispatchResult { + ensure_root(origin)?; + if let Some(threshold) = threshold { + >::set(threshold); + } else { + >::kill(); + } + Self::deposit_event(Event::NewCollatorKickThreshold { length_in_blocks: threshold }); + Ok(()) + } } #[pallet::pallet] --- a/primitives/common/src/constants.rs +++ b/primitives/common/src/constants.rs @@ -46,6 +46,8 @@ pub const EXISTENTIAL_DEPOSIT: u128 = 0; /// Amount of Balance reserved for candidate registration. pub const GENESIS_LICENSE_BOND: u128 = 1_000_000_000_000 * UNIQUE; +/// Amount of maximum collators for Collator Selection. +pub const MAX_COLLATORS: u32 = 10; /// How long a periodic session lasts in blocks. pub const SESSION_LENGTH: BlockNumber = HOURS; --- a/runtime/common/config/pallets/collator_selection.rs +++ b/runtime/common/config/pallets/collator_selection.rs @@ -17,14 +17,14 @@ use frame_support::{parameter_types, PalletId}; use frame_system::EnsureRoot; use crate::{ - AccountId, BlockNumber, Runtime, RuntimeEvent, Balances, Aura, Session, SessionKeys, - CollatorSelection, config::pallets::TreasuryAccountId, + AccountId, Balance, Balances, BlockNumber, Runtime, RuntimeEvent, Aura, Session, SessionKeys, + CollatorSelection, Treasury, + config::pallets::{MaxCollators, SessionPeriod, TreasuryAccountId}, }; use sp_runtime::Perbill; -use up_common::constants::*; +use up_common::constants::{UNIQUE, MILLIUNIQUE}; parameter_types! { - pub const SessionPeriod: BlockNumber = SESSION_LENGTH; pub const SessionOffset: BlockNumber = 0; } @@ -55,13 +55,11 @@ parameter_types! { pub const PotId: PalletId = PalletId(*b"PotStake"); - pub const MaxCollators: u32 = 10; pub const SlashRatio: Perbill = Perbill::from_percent(100); } impl pallet_collator_selection::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type Currency = Balances; // We allow root only to execute privileged collator selection operations. type UpdateOrigin = EnsureRoot; type TreasuryAccountId = TreasuryAccountId; --- a/runtime/common/config/pallets/mod.rs +++ b/runtime/common/config/pallets/mod.rs @@ -25,7 +25,7 @@ }, Runtime, RuntimeEvent, RuntimeCall, Balances, }; -use frame_support::traits::{ConstU32, ConstU64}; +use frame_support::traits::{ConstU32, ConstU64, ConstU128}; use up_common::{ types::{AccountId, Balance, BlockNumber}, constants::*, @@ -104,11 +104,20 @@ parameter_types! { pub AppPromotionDailyRate: Perbill = Perbill::from_rational(5u32, 10_000); + pub const MaxCollators: u32 = MAX_COLLATORS; + pub const SessionPeriod: BlockNumber = SESSION_LENGTH; pub const DayRelayBlocks: BlockNumber = RELAY_DAYS; } + impl pallet_configuration::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; type DefaultWeightToFeeCoefficient = ConstU32<{ up_common::constants::WEIGHT_TO_FEE_COEFF }>; type DefaultMinGasPrice = ConstU64<{ up_common::constants::MIN_GAS_PRICE }>; + type DefaultCollatorSelectionMaxCollators = MaxCollators; + type DefaultCollatorSelectionKickThreshold = SessionPeriod; + type DefaultCollatorSelectionLicenseBond = + ConstU128<{ up_common::constants::GENESIS_LICENSE_BOND }>; type MaxXcmAllowedLocations = ConstU32<16>; type AppPromotionDailyRate = AppPromotionDailyRate; type DayRelayBlocks = DayRelayBlocks; --- a/runtime/common/construct_runtime/mod.rs +++ b/runtime/common/construct_runtime/mod.rs @@ -69,7 +69,7 @@ // #[runtimes(opal)] // Scheduler: pallet_unique_scheduler_v2::{Pallet, Call, Storage, Event} = 62, - Configuration: pallet_configuration::{Pallet, Call, Storage} = 63, + Configuration: pallet_configuration::{Pallet, Call, Storage, Event} = 63, Charging: pallet_charge_transaction::{Pallet, Call, Storage } = 64, // ContractHelpers: pallet_contract_helpers::{Pallet, Call, Storage} = 65, --- a/runtime/common/maintenance.rs +++ b/runtime/common/maintenance.rs @@ -85,6 +85,12 @@ Err(TransactionValidityError::Invalid(InvalidTransaction::Call)) } + #[cfg(feature = "collator-selection")] + RuntimeCall::CollatorSelection(_) + | RuntimeCall::Authorship(_) + | RuntimeCall::Session(_) + | RuntimeCall::Identity(_) => Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), + #[cfg(feature = "pallet-test-utils")] RuntimeCall::TestUtils(_) => Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), @@ -110,7 +116,7 @@ ) -> TransactionValidity { if Maintenance::is_enabled() { match call { - RuntimeCall::EVM(_) | RuntimeCall::Ethereum(_) | RuntimeCall::EvmMigration(_) => { + RuntimeCall::EVM(_) | RuntimeCall::Ethereum(_) | RuntimeCall::DataManagement(_) => { Err(TransactionValidityError::Invalid(InvalidTransaction::Call)) } _ => Ok(ValidTransaction::default()), --- a/runtime/common/mod.rs +++ b/runtime/common/mod.rs @@ -186,13 +186,9 @@ #[cfg(feature = "collator-selection")] { use frame_support::{BoundedVec, storage::migration}; - use sp_runtime::{ - traits::{OpaqueKeys, Saturating}, - RuntimeAppPublic, - }; + use sp_runtime::{traits::OpaqueKeys, RuntimeAppPublic}; use pallet_session::SessionManager; - use up_common::constants::{GENESIS_LICENSE_BOND, SESSION_LENGTH}; - use crate::config::pallets::collator_selection::MaxCollators; + use crate::config::pallets::MaxCollators; let mut weight = ::DbWeight::get().reads(1); @@ -241,9 +237,6 @@ .expect("Existing collators/invulnerables are more than MaxCollators"); >::put(bounded_invulnerables); - >::put(SESSION_LENGTH); - >::put(MaxCollators::get()); - >::put(GENESIS_LICENSE_BOND); let keys = invulnerables .into_iter() --- a/runtime/common/tests/mod.rs +++ b/runtime/common/tests/mod.rs @@ -64,9 +64,6 @@ let cfg = GenesisConfig { collator_selection: CollatorSelectionConfig { - desired_collators: 2, - license_bond: 10, - kick_threshold: 10, invulnerables, }, session: SessionConfig { keys }, --- a/tests/src/collatorSelection.seqtest.ts +++ b/tests/src/collatorSelection.seqtest.ts @@ -17,8 +17,6 @@ import {IKeyringPair} from '@polkadot/types/types'; import {usingPlaygrounds, expect, itSub, Pallets, requirePalletsOrSkip} from './util'; -const MAX_INVULNERABLES = 10; - async function resetInvulnerables() { await usingPlaygrounds(async (helper, privateKey) => { const superuser = await privateKey('//Alice'); @@ -31,7 +29,7 @@ let nonce = await helper.chain.getNonce(alice.address); // In case there are too many invulnerables already, remove some of them, leaving space for Alice and Bob. - if (invulnerables.length + 2 >= MAX_INVULNERABLES) { + if (invulnerables.length + 2 >= helper.collatorSelection.maxCollators()) { await Promise.all([ helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerables.pop()], true, {nonce: nonce++}), helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerables.pop()], true, {nonce: nonce++}), @@ -409,7 +407,7 @@ // 28 non-functioning collators, teehee. const invulnerablesLength = (await helper.collatorSelection.getInvulnerables()).length; - const invulnerablesUntilLimit = MAX_INVULNERABLES - invulnerablesLength; + const invulnerablesUntilLimit = helper.collatorSelection.maxCollators() - invulnerablesLength; const newInvulnerables = await helper.arrange.createAccounts(Array(invulnerablesUntilLimit).fill(10n), superuser); const [lastInvulnerable] = await helper.arrange.createAccounts([10n], superuser); --- a/tests/src/util/playgrounds/unique.dev.ts +++ b/tests/src/util/playgrounds/unique.dev.ts @@ -65,6 +65,7 @@ arrange: ArrangeGroup; wait: WaitGroup; admin: AdminGroup; + session: SessionGroup; testUtils: TestUtilGroup; constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) { @@ -75,6 +76,7 @@ this.wait = new WaitGroup(this); this.admin = new AdminGroup(this); this.testUtils = new TestUtilGroup(this); + this.session = new SessionGroup(this); } async connect(wsEndpoint: string, _listeners?: any): Promise { @@ -456,14 +458,14 @@ console.log(`Waiting for ${sessionCount} new session${sessionCount > 1 ? 's' : ''}.` + ' This might take a while -- check SessionPeriod in pallet_session::Config for session time.'); - const expectedSessionIndex = await this.helper.session.getIndex() + sessionCount; + const expectedSessionIndex = await (this.helper as DevUniqueHelper).session.getIndex() + sessionCount; let currentSessionIndex = -1; while (currentSessionIndex < expectedSessionIndex) { // eslint-disable-next-line no-async-promise-executor currentSessionIndex = await this.withTimeout(new Promise(async (resolve) => { await this.newBlocks(1); - const res = this.helper.session.getIndex(); + const res = await (this.helper as DevUniqueHelper).session.getIndex(); resolve(res); }), blockTimeout, 'The chain has stopped producing blocks!'); } @@ -552,6 +554,36 @@ } } +class SessionGroup { + helper: ChainHelperBase; + + constructor(helper: ChainHelperBase) { + this.helper = helper; + } + + //todo:collator documentation + async getIndex(): Promise { + return (await this.helper.callRpc('api.query.session.currentIndex')).toNumber(); + } + + newSessions(sessionCount = 1, blockTimeout = 24000): Promise { + return (this.helper as DevUniqueHelper).wait.newSessions(sessionCount, blockTimeout); + } + + setOwnKeys(signer: TSigner, key: string) { + return this.helper.executeExtrinsic( + signer, + 'api.tx.session.setKeys', + [key, '0x0'], + true, + ); + } + + setOwnKeysFromAddress(signer: TSigner) { + return this.setOwnKeys(signer, '0x' + Buffer.from(signer.addressRaw).toString('hex')); + } +} + class TestUtilGroup { helper: DevUniqueHelper; --- a/tests/src/util/playgrounds/unique.ts +++ b/tests/src/util/playgrounds/unique.ts @@ -45,7 +45,6 @@ import {RuntimeDispatchInfo} from '@polkadot/types/interfaces'; import type {Vec} from '@polkadot/types-codec'; import {FrameSystemEventRecord} from '@polkadot/types/lookup'; -import {DevUniqueHelper} from './unique.dev'; export class CrossAccountId implements ICrossAccountId { Substrate?: TSubstrateAccount; @@ -376,7 +375,6 @@ children: ChainHelperBase[]; address: AddressGroup; chain: ChainGroup; - session: SessionGroup; constructor(logger?: ILogger, helperBase?: any) { this.helperBase = helperBase; @@ -392,7 +390,6 @@ this.children = []; this.address = new AddressGroup(this); this.chain = new ChainGroup(this); - this.session = new SessionGroup(this); } clone(helperCls: ChainHelperBaseConstructor, options: {[key: string]: any} = {}) { @@ -2642,30 +2639,6 @@ blocksNum, options, }) as T; - } -} - -class SessionGroup extends HelperGroup { - //todo:collator documentation - async getIndex(): Promise { - return (await this.helper.callRpc('api.query.session.currentIndex')).toNumber(); - } - - newSessions(sessionCount = 1, blockTimeout = 24000): Promise { - return (this.helper as DevUniqueHelper).wait.newSessions(sessionCount, blockTimeout); - } - - setOwnKeys(signer: TSigner, key: string) { - return this.helper.executeExtrinsic( - signer, - 'api.tx.session.setKeys', - [key, '0x0'], - true, - ); - } - - setOwnKeysFromAddress(signer: TSigner) { - return this.setOwnKeys(signer, '0x' + Buffer.from(signer.addressRaw).toString('hex')); } } @@ -2683,12 +2656,21 @@ return (await this.helper.callRpc('api.query.collatorSelection.invulnerables')).map((x: any) => x.toHuman()); } + /** and also total max invulnerables */ + maxCollators(): number { + return (this.helper.getApi().consts.configuration.defaultCollatorSelectionMaxCollators.toJSON() as number); + } + + async getDesiredCollators(): Promise { + return (await this.helper.callRpc('api.query.configuration.collatorSelectionDesiredCollatorsOverride')).toNumber(); + } + setLicenseBond(signer: TSigner, amount: bigint) { - return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.setLicenseBond', [amount]); + return this.helper.executeExtrinsic(signer, 'api.tx.configuration.setCollatorSelectionLicenseBond', [amount]); } async getLicenseBond(): Promise { - return (await this.helper.callRpc('api.query.collatorSelection.licenseBond')).toBigInt(); + return (await this.helper.callRpc('api.query.configuration.collatorSelectionLicenseBondOverride')).toBigInt(); } obtainLicense(signer: TSigner) { @@ -2704,7 +2686,7 @@ } async hasLicense(address: string): Promise { - return (await this.helper.callRpc('api.query.collatorSelection.licenses', [address])).toBigInt(); + return (await this.helper.callRpc('api.query.collatorSelection.licenseDepositOf', [address])).toBigInt(); } onboard(signer: TSigner) {