difftreelog
feat(collator-selection) method refactoring + unit tests complete
in: master
9 files changed
pallets/collator-selection/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/collator-selection/src/benchmarking.rs
+++ b/pallets/collator-selection/src/benchmarking.rs
@@ -130,7 +130,7 @@
where_clause { where T: pallet_authorship::Config + session::Config }
set_invulnerables {
- let b in 1 .. T::MaxInvulnerables::get();
+ let b in 1 .. T::MaxCollators::get();
let new_invulnerables = register_validators::<T>(b);
let origin = T::UpdateOrigin::successful_origin();
}: {
@@ -142,16 +142,16 @@
assert_last_event::<T>(Event::NewInvulnerables{invulnerables: new_invulnerables}.into());
}
- set_desired_candidates {
+ set_desired_collators {
let max: u32 = 999;
let origin = T::UpdateOrigin::successful_origin();
}: {
assert_ok!(
- <CollatorSelection<T>>::set_desired_candidates(origin, max.clone())
+ <CollatorSelection<T>>::set_desired_collators(origin, max.clone())
);
}
verify {
- assert_last_event::<T>(Event::NewDesiredCandidates{desired_candidates: max}.into());
+ assert_last_event::<T>(Event::NewDesiredCollators{desired_collators: max}.into());
}
set_license_bond {
@@ -169,10 +169,10 @@
// worse case is when we have all the max-candidate slots filled except one, and we fill that
// one.
register_as_candidate {
- let c in 1 .. T::MaxCandidates::get();
+ let c in 1 .. T::MaxCollators::get();
<LicenseBond<T>>::put(T::Currency::minimum_balance());
- <DesiredCandidates<T>>::put(c + 1);
+ <DesiredCollators<T>>::put(c + 1);
register_validators::<T>(c);
register_candidates::<T>(c);
@@ -194,9 +194,9 @@
// worse case is the last candidate leaving.
leave_intent {
- let c in (T::MinCandidates::get() + 1) .. T::MaxCandidates::get();
+ let c in (T::MinCandidates::get() + 1) .. T::MaxCollators::get();
<LicenseBond<T>>::put(T::Currency::minimum_balance());
- <DesiredCandidates<T>>::put(c);
+ <DesiredCollators<T>>::put(c);
register_validators::<T>(c);
register_candidates::<T>(c);
@@ -230,11 +230,11 @@
// worst case for new session.
new_session {
- let r in 1 .. T::MaxCandidates::get();
- let c in 1 .. T::MaxCandidates::get();
+ let r in 1 .. T::MaxCollators::get();
+ let c in 1 .. T::MaxCollators::get();
<LicenseBond<T>>::put(T::Currency::minimum_balance());
- <DesiredCandidates<T>>::put(c);
+ <DesiredCollators<T>>::put(c);
frame_system::Pallet::<T>::set_block_number(0u32.into());
register_validators::<T>(c);
pallets/collator-selection/src/lib.rsdiffbeforeafterboth98 dispatch::{DispatchClass, DispatchResultWithPostInfo},98 dispatch::{DispatchClass, DispatchResultWithPostInfo},99 inherent::Vec,99 inherent::Vec,100 pallet_prelude::*,100 pallet_prelude::*,101 sp_runtime::{101 sp_runtime::traits::{AccountIdConversion, CheckedSub, Saturating, Zero},102 traits::{AccountIdConversion, CheckedSub, Saturating, Zero},103 RuntimeDebug,104 },105 traits::{102 traits::{106 Currency, EnsureOrigin, ExistenceRequirement::KeepAlive, ReservableCurrency,103 Currency, EnsureOrigin, ExistenceRequirement::KeepAlive, ReservableCurrency,107 ValidatorRegistration,104 ValidatorRegistration,146 /// Account Identifier from which the internal Pot is generated.143 /// Account Identifier from which the internal Pot is generated.147 type PotId: Get<PalletId>;144 type PotId: Get<PalletId>;148145149 /// Maximum number of candidates that we should have. This is enforced in code.146 /// Maximum number of candidates and invulnerables that we should have. This is enforced in code.150 ///151 /// This does not take into account the invulnerables.152 type MaxCandidates: Get<u32>;147 type MaxCollators: Get<u32>;153154 /// Minimum number of candidates that we should have. This is used for disaster recovery.155 ///156 /// This does not take into account the invulnerables.157 type MinCandidates: Get<u32>;158159 /// Maximum number of invulnerables. This is enforced in code.160 type MaxInvulnerables: Get<u32>;161148162 /// If kicked, how much of the collator's deposit will be slashed and sent to the slash destination.149 /// If kicked, how much of the collator's deposit will be slashed and sent to the slash destination.163 type SlashRatio: Get<Perbill>;150 type SlashRatio: Get<Perbill>;177 type WeightInfo: WeightInfo;164 type WeightInfo: WeightInfo;178 }165 }179180 /// Basic information about a collation candidate.181 #[derive(182 PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, scale_info::TypeInfo, MaxEncodedLen,183 )]184 pub struct LicenseInfo<AccountId, Balance> {185 /// Account identifier.186 pub who: AccountId,187 /// Reserved deposit.188 pub deposit: Balance,189 }190166191 #[pallet::pallet]167 #[pallet::pallet]192 #[pallet::generate_store(pub(super) trait Store)]168 #[pallet::generate_store(pub(super) trait Store)]196 #[pallet::storage]172 #[pallet::storage]197 #[pallet::getter(fn invulnerables)]173 #[pallet::getter(fn invulnerables)]198 pub type Invulnerables<T: Config> =174 pub type Invulnerables<T: Config> =199 StorageValue<_, BoundedVec<T::AccountId, T::MaxInvulnerables>, ValueQuery>;175 StorageValue<_, BoundedVec<T::AccountId, T::MaxCollators>, ValueQuery>;200176201 /// The (community) collation license holders.177 /// The (community) collation license holders.202 #[pallet::storage]178 #[pallet::storage]209 #[pallet::getter(fn candidates)]185 #[pallet::getter(fn candidates)]210 pub type Candidates<T: Config> = StorageValue<186 pub type Candidates<T: Config> = StorageValue<211 _,187 _,212 BoundedVec<T::AccountId, T::MaxCandidates>, //LicenseInfo<T::AccountId, BalanceOf<T>>, T::MaxCandidates>, // license ID?188 BoundedVec<T::AccountId, T::MaxCollators>, //LicenseInfo<T::AccountId, BalanceOf<T>>, T::MaxCollators>, // license ID?213 ValueQuery,189 ValueQuery,214 >;190 >;215191228204229 /// Desired number of candidates.205 /// Desired number of candidates.230 ///206 ///231 /// This should ideally always be less than [`Config::MaxCandidates`] for weights to be correct.207 /// This should ideally always be less than [`Config::MaxCollators`] for weights to be correct.232 #[pallet::storage]208 #[pallet::storage]233 #[pallet::getter(fn desired_candidates)]209 #[pallet::getter(fn desired_collators)]234 pub type DesiredCandidates<T> = StorageValue<_, u32, ValueQuery>;210 pub type DesiredCollators<T> = StorageValue<_, u32, ValueQuery>;235211236 /// Fixed amount to deposit to become a collator.212 /// Fixed amount to deposit to become a collator.237 ///213 ///245 pub invulnerables: Vec<T::AccountId>,221 pub invulnerables: Vec<T::AccountId>,246 pub license_bond: BalanceOf<T>,222 pub license_bond: BalanceOf<T>,247 pub kick_threshold: T::BlockNumber,223 pub kick_threshold: T::BlockNumber,248 pub desired_candidates: u32,224 pub desired_collators: u32,249 }225 }250226251 #[cfg(feature = "std")]227 #[cfg(feature = "std")]255 invulnerables: Default::default(),231 invulnerables: Default::default(),256 license_bond: Default::default(),232 license_bond: Default::default(),257 kick_threshold: T::BlockNumber::one(),233 kick_threshold: T::BlockNumber::one(),258 desired_candidates: Default::default(),234 desired_collators: Default::default(),259 }235 }260 }236 }261 }237 }273 );249 );274250275 let bounded_invulnerables =251 let bounded_invulnerables =276 BoundedVec::<_, T::MaxInvulnerables>::try_from(self.invulnerables.clone())252 BoundedVec::<_, T::MaxCollators>::try_from(self.invulnerables.clone())277 .expect("genesis invulnerables are more than T::MaxInvulnerables");253 .expect("genesis invulnerables are more than T::MaxCollators");278 assert!(254 assert!(279 T::MaxCandidates::get() >= self.desired_candidates,255 T::MaxCollators::get() >= self.desired_collators,280 "genesis desired_candidates are more than T::MaxCandidates",256 "genesis desired_collators are more than T::MaxCollators",281 );257 );282258283 <DesiredCandidates<T>>::put(&self.desired_candidates);259 <DesiredCollators<T>>::put(self.desired_collators);284 <LicenseBond<T>>::put(&self.license_bond);260 <LicenseBond<T>>::put(self.license_bond);285 <KickThreshold<T>>::put(&self.kick_threshold);261 <KickThreshold<T>>::put(self.kick_threshold);286 <Invulnerables<T>>::put(bounded_invulnerables);262 <Invulnerables<T>>::put(bounded_invulnerables);287 }263 }288 }264 }289265290 #[pallet::event]266 #[pallet::event]291 #[pallet::generate_deposit(pub(super) fn deposit_event)]267 #[pallet::generate_deposit(pub(super) fn deposit_event)]292 pub enum Event<T: Config> {268 pub enum Event<T: Config> {293 NewDesiredCandidates {269 NewDesiredCollators {294 desired_candidates: u32,270 desired_collators: u32,295 },271 },296 NewLicenseBond {272 NewLicenseBond {297 bond_amount: BalanceOf<T>,273 bond_amount: BalanceOf<T>,326 pub enum Error<T> {302 pub enum Error<T> {327 /// Too many candidates303 /// Too many candidates328 TooManyCandidates,304 TooManyCandidates,329 /// Too few candidates330 TooFewCandidates,331 /// Unknown error305 /// Unknown error332 Unknown,306 Unknown,333 /// Permission issue307 /// Permission issue334 Permission,308 Permission,335 /// User already holds license to collate309 /// User already holds license to collate336 AlreadyLicenseHolder,310 AlreadyHoldingLicense,337 /// User does not hold a license to collate311 /// User does not hold a license to collate338 NoLicense,312 NoLicense,339 /// User is already a candidate313 /// User is already a candidate360 #[pallet::call]334 #[pallet::call]361 impl<T: Config> Pallet<T> {335 impl<T: Config> Pallet<T> {362 /// Add a collator to the list of invulnerable (fixed) collators.336 /// Add a collator to the list of invulnerable (fixed) collators.363 #[pallet::weight(T::WeightInfo::set_invulnerables(1 as u32))] // todo:collator weight337 #[pallet::weight(T::WeightInfo::set_invulnerables(1u32))] // todo:collator weight364 pub fn add_invulnerable(338 pub fn add_invulnerable(365 origin: OriginFor<T>,339 origin: OriginFor<T>,366 new: T::AccountId,340 new: T::AccountId,379 return Ok(().into());353 return Ok(().into());380 }354 }381382 // todo:collator check license holders, release moneys, promotion!383 // force_release_license? Error::<T>::lreadyLicenseHolder?384355385 <Invulnerables<T>>::try_append(new.clone())356 <Invulnerables<T>>::try_append(new.clone())386 .map_err(|_| Error::<T>::TooManyInvulnerables)?;357 .map_err(|_| Error::<T>::TooManyInvulnerables)?;358359 // try to offboard the new invulnerable if it was a collator candidate before360 let _ = Self::try_remove_candidate(&new);361387 Self::deposit_event(Event::InvulnerableAdded { invulnerable: new });362 Self::deposit_event(Event::InvulnerableAdded { invulnerable: new });388 Ok(().into())363 Ok(().into())417 Ok(().into())392 Ok(().into())418 }393 }419394420 /// Set the ideal number of collators (not including the invulnerables).395 /// Set the ideal number of collators. If lowering this number,421 /// If lowering this number, then the number of running collators could be higher than this figure.396 /// then the number of running collators could be higher than this figure.422 /// Aside from that edge case, there should be no other way to have more collators than the desired number.397 /// Aside from that edge case, there should be no other way to have more collators than the desired number.423 #[pallet::weight(T::WeightInfo::set_desired_candidates())]398 #[pallet::weight(T::WeightInfo::set_desired_collators())]424 pub fn set_desired_candidates(399 pub fn set_desired_collators(origin: OriginFor<T>, max: u32) -> DispatchResultWithPostInfo {425 origin: OriginFor<T>,426 max: u32,427 ) -> DispatchResultWithPostInfo {428 T::UpdateOrigin::ensure_origin(origin)?;400 T::UpdateOrigin::ensure_origin(origin)?;429 // we trust origin calls, this is just a for more accurate benchmarking401 // we trust origin calls, this is just a for more accurate benchmarking430 if max > T::MaxCandidates::get() {402 if max > T::MaxCollators::get() {431 log::warn!("max > T::MaxCandidates; you might need to run benchmarks again");403 log::warn!("max > T::MaxCollators; you might need to run benchmarks again");432 }404 }433 <DesiredCandidates<T>>::put(&max);405 <DesiredCollators<T>>::put(max);434 Self::deposit_event(Event::NewDesiredCandidates {406 Self::deposit_event(Event::NewDesiredCollators {435 desired_candidates: max,407 desired_collators: max,436 });408 });437 Ok(().into())409 Ok(().into())438 }410 }444 bond: BalanceOf<T>,416 bond: BalanceOf<T>,445 ) -> DispatchResultWithPostInfo {417 ) -> DispatchResultWithPostInfo {446 T::UpdateOrigin::ensure_origin(origin)?;418 T::UpdateOrigin::ensure_origin(origin)?;447 <LicenseBond<T>>::put(&bond);419 <LicenseBond<T>>::put(bond);448 Self::deposit_event(Event::NewLicenseBond { bond_amount: bond });420 Self::deposit_event(Event::NewLicenseBond { bond_amount: bond });449 Ok(().into())421 Ok(().into())450 }422 }470 /// (a) already have registered session keys and (b) be able to reserve the `LicenseBond`.442 /// (a) already have registered session keys and (b) be able to reserve the `LicenseBond`.471 ///443 ///472 /// This call is not available to `Invulnerable` collators.444 /// This call is not available to `Invulnerable` collators.473 #[pallet::weight(T::WeightInfo::register_as_candidate(T::MaxCandidates::get()))] // todo:collator weight445 #[pallet::weight(T::WeightInfo::register_as_candidate(T::MaxCollators::get()))] // todo:collator weight474 pub fn get_license(origin: OriginFor<T>) -> DispatchResultWithPostInfo {446 pub fn get_license(origin: OriginFor<T>) -> DispatchResultWithPostInfo {475 // register_as_candidate447 // register_as_candidate476 let who = ensure_signed(origin)?;448 let who = ensure_signed(origin)?;477449478 if Licenses::<T>::contains_key(&who) {450 if Licenses::<T>::contains_key(&who) {479 return Ok(().into());451 return Err(Error::<T>::AlreadyHoldingLicense.into());480 }452 }481453482 ensure!(454 /*ensure!(483 !Self::invulnerables().contains(&who),455 !Self::invulnerables().contains(&who),484 Error::<T>::AlreadyInvulnerable456 Error::<T>::AlreadyInvulnerable485 );457 );*/486458487 let validator_key = T::ValidatorIdOf::convert(who.clone())459 let validator_key = T::ValidatorIdOf::convert(who.clone())488 .ok_or(Error::<T>::NoAssociatedValidatorId)?;460 .ok_or(Error::<T>::NoAssociatedValidatorId)?;501 T::Currency::reserve(&who, deposit)?;473 T::Currency::reserve(&who, deposit)?;502 Licenses::<T>::insert(who.clone(), deposit);474 Licenses::<T>::insert(who.clone(), deposit);503475504 /*let current_count =476 /*let current_count =505 <Licenses<T>>::try_mutate(|licenses| -> Result<usize, DispatchError> {477 <Licenses<T>>::try_mutate(|licenses| -> Result<usize, DispatchError> {506 if T::OriginPrivilegeCmp::cmp_privilege(&origin, &scheduled.origin) {478 if T::OriginPrivilegeCmp::cmp_privilege(&origin, &scheduled.origin) {507 return Err(BadOrigin.into());479 return Err(BadOrigin.into());508 }480 }509 if candidates.iter().any(|candidate| *candidate == who) {481 if candidates.iter().any(|candidate| *candidate == who) {510 Err(Error::<T>::AlreadyLicenseHolder)?482 Err(Error::<T>::AlreadyHoldingLicense)?511 } else {483 } else {512 T::Currency::reserve(&who, deposit)?;484 T::Currency::reserve(&who, deposit)?;513 candidates485 candidates514 .try_push(incoming)486 .try_push(incoming)515 .map_err(|_| Error::<T>::TooManyCandidates)?;487 .map_err(|_| Error::<T>::TooManyCandidates)?;516 <LastAuthoredBlock<T>>::insert(488 <LastAuthoredBlock<T>>::insert(517 who.clone(),489 who.clone(),518 frame_system::Pallet::<T>::block_number() + Self::kick_threshold(),490 frame_system::Pallet::<T>::block_number() + Self::kick_threshold(),519 );491 );520 Ok(candidates.len())492 Ok(candidates.len())521 }493 }522 })?;*/494 })?;*/523495524 Self::deposit_event(Event::LicenseObtained {496 Self::deposit_event(Event::LicenseObtained {525 account_id: who,497 account_id: who,532 /// The account must already hold a license, and cannot offboard immediately during a session.504 /// The account must already hold a license, and cannot offboard immediately during a session.533 ///505 ///534 /// This call is not available to `Invulnerable` collators.506 /// This call is not available to `Invulnerable` collators.535 #[pallet::weight(T::WeightInfo::register_as_candidate(T::MaxCandidates::get()))] // todo:collator weight507 #[pallet::weight(T::WeightInfo::register_as_candidate(T::MaxCollators::get()))] // todo:collator weight536 pub fn onboard(origin: OriginFor<T>) -> DispatchResultWithPostInfo {508 pub fn onboard(origin: OriginFor<T>) -> DispatchResultWithPostInfo {537 // register_as_candidate509 // register_as_candidate538 let who = ensure_signed(origin)?;510 let who = ensure_signed(origin)?;539511540 // ensure the user obtained the license.512 // ensure the user obtained the license.541 ensure!(Licenses::<T>::contains_key(&who), Error::<T>::NoLicense);513 ensure!(Licenses::<T>::contains_key(&who), Error::<T>::NoLicense);542 // ensure we are below limit.514 // ensure we are below limit.543 let length = <Candidates<T>>::decode_len().unwrap_or_default();515 let length = <Candidates<T>>::decode_len().unwrap_or_default()516 + <Invulnerables<T>>::decode_len().unwrap_or_default();544 ensure!(517 ensure!(545 (length as u32) < Self::desired_candidates(),518 (length as u32) < Self::desired_collators(),546 Error::<T>::TooManyCandidates519 Error::<T>::TooManyCandidates547 );520 );548 // todo:collator really need it?521 // todo:collator really need it?551 Error::<T>::AlreadyInvulnerable524 Error::<T>::AlreadyInvulnerable552 );525 );553526554 let deposit = Self::license_bond();555 // First authored block is current block plus kick threshold to handle session delay556 /*let incoming = LicenseInfo {527 /*let incoming = LicenseInfo {557 who: who.clone(),528 who: who.clone(),558 deposit,529 deposit,563 if candidates.iter().any(|candidate| *candidate == who) {534 if candidates.iter().any(|candidate| *candidate == who) {564 Err(Error::<T>::AlreadyCandidate)?535 Err(Error::<T>::AlreadyCandidate)?565 } else {536 } else {566 T::Currency::reserve(&who, deposit)?;567 candidates537 candidates568 .try_push(who.clone())538 .try_push(who.clone())569 .map_err(|_| Error::<T>::TooManyCandidates)?;539 .map_err(|_| Error::<T>::TooManyCandidates)?;540 // First authored block is current block plus kick threshold to handle session delay570 <LastAuthoredBlock<T>>::insert(541 <LastAuthoredBlock<T>>::insert(571 who.clone(),542 who.clone(),572 frame_system::Pallet::<T>::block_number() + Self::kick_threshold(),543 frame_system::Pallet::<T>::block_number() + Self::kick_threshold(),583 /// session change. The license to `onboard` later at any other time will remain.554 /// session change. The license to `onboard` later at any other time will remain.584 ///555 ///585 /// This call will fail if the total number of candidates would drop below `MinCandidates`. todo:collator maybe not556 /// This call will fail if the total number of candidates would drop below `MinCandidates`. todo:collator maybe not586 #[pallet::weight(T::WeightInfo::leave_intent(T::MaxCandidates::get()))] // todo:collator weight557 #[pallet::weight(T::WeightInfo::leave_intent(T::MaxCollators::get()))] // todo:collator weight587 pub fn offboard(origin: OriginFor<T>) -> DispatchResultWithPostInfo {558 pub fn offboard(origin: OriginFor<T>) -> DispatchResultWithPostInfo {588 // leave_intent559 // leave_intent589 let who = ensure_signed(origin)?;560 let who = ensure_signed(origin)?;590 // todo:collator invulnerables and candidates should count against min candidates together561 /* todo:collator invulnerables and candidates should count against min candidates together591 ensure!(562 ensure!(592 Self::candidates().len() as u32 > T::MinCandidates::get(),563 Self::candidates().len() as u32 > T::MinCandidates::get(),593 Error::<T>::TooFewCandidates564 Error::<T>::TooFewCandidates594 );565 );*/595 let current_count = Self::try_remove_candidate(&who)?;566 let current_count = Self::try_remove_candidate(&who)?;596567597 Ok(Some(T::WeightInfo::leave_intent(current_count as u32)).into())568 Ok(Some(T::WeightInfo::leave_intent(current_count as u32)).into()) // todo:collator weight598 }569 }599570600 /// Forfeit `origin`'s own license. The `LicenseBond` will be unreserved immediately.571 /// Forfeit `origin`'s own license. The `LicenseBond` will be unreserved immediately.601 ///572 ///602 /// This call is not available to `Invulnerable` collators.573 /// This call is not available to `Invulnerable` collators.603 #[pallet::weight(T::WeightInfo::leave_intent(T::MaxCandidates::get()))] // todo:collator weight574 #[pallet::weight(T::WeightInfo::leave_intent(T::MaxCollators::get()))] // todo:collator weight604 pub fn release_license(origin: OriginFor<T>) -> DispatchResultWithPostInfo {575 pub fn release_license(origin: OriginFor<T>) -> DispatchResultWithPostInfo {605 // leave_intent576 // leave_intent606 let who = ensure_signed(origin)?;577 let who = ensure_signed(origin)?;607 // let current_count = Self::try_remove_candidate(&who, false)?;578608 Self::try_release_license(&who, false)?;579 let current_count = Self::try_remove_candidate_and_release_license(&who, false, true)?;609580610 Ok(().into())581 Ok(Some(T::WeightInfo::leave_intent(current_count as u32)).into()) // todo:collator weight611 }582 }612583613 /// Force deregister `origin` as a collator candidate as a governing authority, and revoke its license.584 /// Force deregister `origin` as a collator candidate as a governing authority, and revoke its license.614 /// Note that the collator can only leave on session change.585 /// Note that the collator can only leave on session change.615 /// The `LicenseBond` will be unreserved and returned immediately.586 /// The `LicenseBond` will be unreserved and returned immediately.616 ///587 ///617 /// This call is not available to `Invulnerable` collators.588 /// This call is not available to `Invulnerable` collators.618 #[pallet::weight(T::WeightInfo::leave_intent(T::MaxCandidates::get()))] // todo:collator weight589 #[pallet::weight(T::WeightInfo::leave_intent(T::MaxCollators::get()))] // todo:collator weight619 pub fn force_release_license(590 pub fn force_revoke_license(620 origin: OriginFor<T>,591 origin: OriginFor<T>,621 who: T::AccountId,592 who: T::AccountId,622 ) -> DispatchResultWithPostInfo {593 ) -> DispatchResultWithPostInfo {623 // leave_intent594 // leave_intent624 T::UpdateOrigin::ensure_origin(origin)?;595 T::UpdateOrigin::ensure_origin(origin)?;625596626 let current_count = Self::try_remove_candidate(&who)?;597 let current_count = Self::try_remove_candidate_and_release_license(&who, false, true)?;627 Self::try_release_license(&who, false)?;628598629 Ok(Some(T::WeightInfo::leave_intent(current_count as u32)).into()) // todo:collator weight599 Ok(Some(T::WeightInfo::leave_intent(current_count as u32)).into()) // todo:collator weight630 }600 }636 T::PotId::get().into_account_truncating()606 T::PotId::get().into_account_truncating()637 }607 }608609 fn try_remove_candidate_and_release_license(610 who: &T::AccountId,611 should_slash: bool,612 ignore_if_not_candidate: bool,613 ) -> Result<usize, DispatchError> {614 let current_count = Self::try_remove_candidate(who);615 let current_count = if ignore_if_not_candidate616 && current_count == Err(Error::<T>::NotCandidate.into())617 {618 <Candidates<T>>::decode_len().unwrap_or_default()619 } else {620 current_count?621 };622 Self::try_release_license(who, should_slash)?;623 Ok(current_count)624 }638625639 /// Removes a candidate from the collator pool for the next session if they exist.626 /// Removes a candidate from the collator pool for the next session if they exist.640 fn try_remove_candidate(who: &T::AccountId) -> Result<usize, DispatchError> {627 fn try_remove_candidate(who: &T::AccountId) -> Result<usize, DispatchError> {657 /// Removes a candidate if they exist and sends them back their deposit, optionally slashed.644 /// Removes a candidate if they exist and sends them back their deposit, optionally slashed.658 fn try_release_license(who: &T::AccountId, should_slash: bool) -> DispatchResult {645 fn try_release_license(who: &T::AccountId, should_slash: bool) -> DispatchResult {659 let mut deposit_returned = BalanceOf::<T>::default();646 let mut deposit_returned = BalanceOf::<T>::default();660 Licenses::<T>::try_mutate_exists(&who, |deposit| -> DispatchResult {647 Licenses::<T>::try_mutate_exists(who, |deposit| -> DispatchResult {661 if let Some(deposit) = deposit.take() {648 if let Some(deposit) = deposit.take() {662 if should_slash {649 if should_slash {663 let slashed = T::SlashRatio::get() * deposit;650 let slashed = T::SlashRatio::get() * deposit;690 ///677 ///691 /// This is done on the fly, as frequent as we are told to do so, as the session manager.678 /// This is done on the fly, as frequent as we are told to do so, as the session manager.692 pub fn assemble_collators(679 pub fn assemble_collators(693 candidates: BoundedVec<T::AccountId, T::MaxCandidates>,680 candidates: BoundedVec<T::AccountId, T::MaxCollators>,694 ) -> Vec<T::AccountId> {681 ) -> Vec<T::AccountId> {695 let mut collators = Self::invulnerables().to_vec();682 let mut collators = Self::invulnerables().to_vec();696 collators.extend(candidates);683 collators.extend(candidates);700 /// Kicks out candidates that did not produce a block in the kick threshold687 /// Kicks out candidates that did not produce a block in the kick threshold701 /// and **confiscates** their deposits to the treasury.688 /// and **confiscates** their deposits to the treasury.702 pub fn kick_stale_candidates(689 pub fn kick_stale_candidates(703 candidates: BoundedVec<T::AccountId, T::MaxCandidates>, //LicenseInfo<T::AccountId, BalanceOf<T>>690 candidates: BoundedVec<T::AccountId, T::MaxCollators>, //LicenseInfo<T::AccountId, BalanceOf<T>>704 ) -> BoundedVec<T::AccountId, T::MaxCandidates> {691 ) -> BoundedVec<T::AccountId, T::MaxCollators> {705 let now = frame_system::Pallet::<T>::block_number();692 let now = frame_system::Pallet::<T>::block_number();706 let kick_threshold = Self::kick_threshold();693 let kick_threshold = Self::kick_threshold();707 candidates694 candidates708 .into_iter()695 .into_iter()709 .filter_map(|c| {696 .filter_map(|c| {710 let last_block = <LastAuthoredBlock<T>>::get(c.clone());697 let last_block = <LastAuthoredBlock<T>>::get(c.clone());711 let since_last = now.saturating_sub(last_block);698 let since_last = now.saturating_sub(last_block);712 if since_last < kick_threshold ||699 if since_last < kick_threshold {713 Self::candidates().len() as u32 <= T::MinCandidates::get()714 {715 Some(c)700 Some(c)716 } else {701 } else {717 let outcome = Self::try_remove_candidate(&c);718 if let Err(why) = outcome {719 log::warn!("Failed to remove candidate {:?}", why);720 debug_assert!(false, "failed to remove candidate {:?}", why);721 return None;722 }723 let outcome = Self::try_release_license(&c, true);702 let outcome = Self::try_remove_candidate_and_release_license(&c, true, false);724 if let Err(why) = outcome {703 if let Err(why) = outcome {725 log::warn!("Failed to release license {:?}", why);704 log::warn!("Failed to kick collator and release license {:?}", why);726 debug_assert!(false, "failed to release license {:?}", why);705 debug_assert!(false, "failed to kick collator and release license {why:?}");727 }706 }728 None707 None729 }708 }pallets/collator-selection/src/mock.rsdiffbeforeafterboth--- a/pallets/collator-selection/src/mock.rs
+++ b/pallets/collator-selection/src/mock.rs
@@ -206,9 +206,7 @@
parameter_types! {
pub const PotId: PalletId = PalletId(*b"PotStake");
- pub const MaxCandidates: u32 = 20;
- pub const MaxInvulnerables: u32 = 20;
- pub const MinCandidates: u32 = 1;
+ pub const MaxCollators: u32 = 20;
pub const MaxAuthorities: u32 = 100_000;
pub const SlashRatio: Perbill = Perbill::one();
}
@@ -230,9 +228,7 @@
type Currency = Balances;
type UpdateOrigin = EnsureSignedBy<RootAccount, u64>;
type PotId = PotId;
- type MaxCandidates = MaxCandidates;
- type MinCandidates = MinCandidates;
- type MaxInvulnerables = MaxInvulnerables;
+ type MaxCollators = MaxCollators;
// type KickThreshold = Period;
type SlashRatio = SlashRatio;
type TreasuryAccountId = ();
@@ -263,9 +259,9 @@
})
.collect::<Vec<_>>();
let collator_selection = collator_selection::GenesisConfig::<Test> {
- desired_candidates: 2,
+ desired_collators: 5,
license_bond: 10,
- kick_threshold: 1,
+ kick_threshold: 10,
invulnerables,
};
let session = pallet_session::GenesisConfig::<Test> { keys };
pallets/collator-selection/src/tests.rsdiffbeforeafterboth--- a/pallets/collator-selection/src/tests.rs
+++ b/pallets/collator-selection/src/tests.rs
@@ -31,7 +31,7 @@
// limitations under the License.
use crate as collator_selection;
-use crate::{mock::*, LicenseInfo, Error};
+use crate::{mock::*, Error};
use frame_support::{
assert_noop, assert_ok,
traits::{Currency, GenesisBuild, OnInitialize},
@@ -39,10 +39,19 @@
use pallet_balances::Error as BalancesError;
use sp_runtime::traits::BadOrigin;
+fn get_license_and_onboard(account_id: <Test as frame_system::Config>::AccountId) {
+ assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(
+ account_id
+ )));
+ assert_ok!(CollatorSelection::onboard(RuntimeOrigin::signed(
+ account_id
+ )));
+}
+
#[test]
fn basic_setup_works() {
new_test_ext().execute_with(|| {
- assert_eq!(CollatorSelection::desired_candidates(), 2);
+ assert_eq!(CollatorSelection::desired_collators(), 5);
assert_eq!(CollatorSelection::license_bond(), 10);
assert!(CollatorSelection::candidates().is_empty());
@@ -51,6 +60,7 @@
}
// todo:collator add more tests later
+// invulnerable after onboard + invulnerables can bypass desired_candidates
#[test]
fn it_should_add_invulnerables() {
@@ -112,21 +122,21 @@
}
#[test]
-fn set_desired_candidates_works() {
+fn set_desired_collators_works() {
new_test_ext().execute_with(|| {
// given
- assert_eq!(CollatorSelection::desired_candidates(), 2);
+ assert_eq!(CollatorSelection::desired_collators(), 5);
// can set
- assert_ok!(CollatorSelection::set_desired_candidates(
+ assert_ok!(CollatorSelection::set_desired_collators(
RuntimeOrigin::signed(RootAccount::get()),
7
));
- assert_eq!(CollatorSelection::desired_candidates(), 7);
+ assert_eq!(CollatorSelection::desired_collators(), 7);
// rejects bad origin
assert_noop!(
- CollatorSelection::set_desired_candidates(RuntimeOrigin::signed(1), 8),
+ CollatorSelection::set_desired_collators(RuntimeOrigin::signed(1), 8),
BadOrigin
);
});
@@ -154,166 +164,246 @@
}
#[test]
-fn cannot_register_candidate_if_too_many() {
+fn cannot_onboard_candidate_with_no_license() {
new_test_ext().execute_with(|| {
- // reset desired candidates:
- <crate::DesiredCandidates<Test>>::put(0);
+ // can't onboard a candidate who did not get a license.
+ assert_noop!(
+ CollatorSelection::onboard(RuntimeOrigin::signed(3)),
+ Error::<Test>::NoLicense,
+ );
+
+ // but give it a license and welcome aboard.
+ assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(3)));
+ assert_ok!(CollatorSelection::onboard(RuntimeOrigin::signed(3)));
+ })
+}
+
+#[test]
+fn cannot_onboard_candidate_if_too_many() {
+ new_test_ext().execute_with(|| {
+ // reset desired candidates
+ <crate::DesiredCollators<Test>>::put(0);
+
+ // can still get a license.
+ assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(4)));
// can't accept anyone anymore.
assert_noop!(
- CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)),
+ CollatorSelection::onboard(RuntimeOrigin::signed(4)),
Error::<Test>::TooManyCandidates,
);
- // reset desired candidates:
- <crate::DesiredCandidates<Test>>::put(1);
- assert_ok!(CollatorSelection::register_as_candidate(
- RuntimeOrigin::signed(4)
- ));
+ // reset desired candidates to invulnerables + 1
+ <crate::DesiredCollators<Test>>::put(3);
+ assert_ok!(CollatorSelection::onboard(RuntimeOrigin::signed(4)));
- // but no more
+ // but no more.
+ assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(5)));
assert_noop!(
- CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)),
+ CollatorSelection::onboard(RuntimeOrigin::signed(5)),
Error::<Test>::TooManyCandidates,
);
})
}
#[test]
-fn cannot_unregister_candidate_if_too_few() {
+fn cannot_obtain_license_if_keys_not_registered() {
new_test_ext().execute_with(|| {
- // reset desired candidates:
- <crate::DesiredCandidates<Test>>::put(1);
- assert_ok!(CollatorSelection::register_as_candidate(
- RuntimeOrigin::signed(4)
- ));
-
- // can not remove too few
+ // can't 7 because keys not registered.
assert_noop!(
- CollatorSelection::leave_intent(RuntimeOrigin::signed(4)),
- Error::<Test>::TooFewCandidates,
+ CollatorSelection::get_license(RuntimeOrigin::signed(7)),
+ Error::<Test>::ValidatorNotRegistered
);
})
}
#[test]
-fn cannot_register_as_candidate_if_invulnerable() {
+fn cannot_obtain_license_if_poor() {
new_test_ext().execute_with(|| {
- assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);
+ assert_eq!(Balances::free_balance(&3), 100);
+ assert_eq!(Balances::free_balance(&33), 0);
- // can't 1 because it is invulnerable.
- assert_noop!(
- CollatorSelection::register_as_candidate(RuntimeOrigin::signed(1)),
- Error::<Test>::AlreadyInvulnerable,
- );
- })
-}
+ // works
+ assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(3)));
-#[test]
-fn cannot_register_as_candidate_if_keys_not_registered() {
- new_test_ext().execute_with(|| {
- // can't 7 because keys not registered.
+ // poor
assert_noop!(
- CollatorSelection::register_as_candidate(RuntimeOrigin::signed(7)),
- Error::<Test>::ValidatorNotRegistered
+ CollatorSelection::get_license(RuntimeOrigin::signed(33)),
+ BalancesError::<Test>::InsufficientBalance,
);
- })
+ });
}
#[test]
-fn cannot_register_dupe_candidate() {
+fn cannot_onboard_dupe_candidate() {
new_test_ext().execute_with(|| {
// can add 3 as candidate
- assert_ok!(CollatorSelection::register_as_candidate(
- RuntimeOrigin::signed(3)
- ));
- let addition = LicenseInfo {
- who: 3,
- deposit: 10,
- };
- assert_eq!(CollatorSelection::candidates(), vec![addition]);
+ get_license_and_onboard(3);
+ assert_eq!(CollatorSelection::licenses(3), 10);
+ assert_eq!(CollatorSelection::candidates(), vec![3]);
assert_eq!(CollatorSelection::last_authored_block(3), 10);
assert_eq!(Balances::free_balance(3), 90);
// but no more
assert_noop!(
- CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)),
+ CollatorSelection::get_license(RuntimeOrigin::signed(3)),
+ Error::<Test>::AlreadyHoldingLicense,
+ );
+ assert_noop!(
+ CollatorSelection::onboard(RuntimeOrigin::signed(3)),
Error::<Test>::AlreadyCandidate,
);
})
}
#[test]
-fn cannot_register_as_candidate_if_poor() {
+fn becoming_candidate_works() {
new_test_ext().execute_with(|| {
+ // given
+ assert_eq!(CollatorSelection::desired_collators(), 5);
+ assert_eq!(CollatorSelection::license_bond(), 10);
+ assert_eq!(CollatorSelection::candidates(), Vec::new());
+ assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);
+
+ // take two endowed, non-invulnerables accounts.
assert_eq!(Balances::free_balance(&3), 100);
- assert_eq!(Balances::free_balance(&33), 0);
+ assert_eq!(Balances::free_balance(&4), 100);
- // works
- assert_ok!(CollatorSelection::register_as_candidate(
- RuntimeOrigin::signed(3)
- ));
+ get_license_and_onboard(3);
+ get_license_and_onboard(4);
+
+ assert_eq!(Balances::free_balance(&3), 90);
+ assert_eq!(Balances::free_balance(&4), 90);
- // poor
- assert_noop!(
- CollatorSelection::register_as_candidate(RuntimeOrigin::signed(33)),
- BalancesError::<Test>::InsufficientBalance,
- );
+ assert_eq!(CollatorSelection::candidates().len(), 2);
});
}
#[test]
-fn register_as_candidate_works() {
+fn cannot_become_candidate_if_invulnerable() {
new_test_ext().execute_with(|| {
- // given
- assert_eq!(CollatorSelection::desired_candidates(), 2);
- assert_eq!(CollatorSelection::license_bond(), 10);
- assert_eq!(CollatorSelection::candidates(), Vec::new());
assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);
- // take two endowed, non-invulnerables accounts.
- assert_eq!(Balances::free_balance(&3), 100);
- assert_eq!(Balances::free_balance(&4), 100);
+ // can obtain a license even if is invulnerable.
+ assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(1)));
+ // but cannot onboard
+ assert_noop!(
+ CollatorSelection::onboard(RuntimeOrigin::signed(1)),
+ Error::<Test>::AlreadyInvulnerable,
+ );
- assert_ok!(CollatorSelection::register_as_candidate(
- RuntimeOrigin::signed(3)
+ // get a license and then become invulnerable.
+ assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(3)));
+ assert_ok!(CollatorSelection::add_invulnerable(
+ RuntimeOrigin::signed(RootAccount::get()),
+ 3
));
- assert_ok!(CollatorSelection::register_as_candidate(
- RuntimeOrigin::signed(4)
+ assert_noop!(
+ CollatorSelection::onboard(RuntimeOrigin::signed(3)),
+ Error::<Test>::AlreadyInvulnerable,
+ );
+ })
+}
+
+#[test]
+fn can_become_invulnerable_if_candidate() {
+ new_test_ext().execute_with(|| {
+ // become a candidate and then become invulnerable.
+ get_license_and_onboard(3);
+ assert_eq!(CollatorSelection::candidates(), vec![3]);
+
+ assert_ok!(CollatorSelection::add_invulnerable(
+ RuntimeOrigin::signed(RootAccount::get()),
+ 3
));
+ // should exclude from candidates, but not revoke the license
+ assert_eq!(CollatorSelection::candidates(), vec![]);
+ assert_eq!(CollatorSelection::licenses(3), 10);
+ assert_eq!(Balances::free_balance(3), 90);
+ });
+}
- assert_eq!(Balances::free_balance(&3), 90);
- assert_eq!(Balances::free_balance(&4), 90);
+#[test]
+fn offboard() {
+ new_test_ext().execute_with(|| {
+ // register a candidate.
+ get_license_and_onboard(3);
+ assert_eq!(Balances::free_balance(3), 90);
+
+ // cannot leave if holds license but not yet candidate.
+ assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(4)));
+ assert_noop!(
+ CollatorSelection::offboard(RuntimeOrigin::signed(4)),
+ Error::<Test>::NotCandidate
+ );
+ // cannot leave if does not hold license.
+ assert_noop!(
+ CollatorSelection::offboard(RuntimeOrigin::signed(5)),
+ Error::<Test>::NotCandidate
+ );
- assert_eq!(CollatorSelection::candidates().len(), 2);
+ // bond is returned - only after releasing the license
+ assert_ok!(CollatorSelection::offboard(RuntimeOrigin::signed(3)));
+ assert_eq!(Balances::free_balance(3), 90);
+ assert_eq!(CollatorSelection::last_authored_block(3), 0);
+ assert_ok!(CollatorSelection::release_license(RuntimeOrigin::signed(3)));
+ assert_eq!(Balances::free_balance(3), 100);
});
}
#[test]
-fn leave_intent() {
+fn release_license() {
new_test_ext().execute_with(|| {
+ // obtain a license to collate and reserve the bond.
+ assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(3)));
+ assert_eq!(Balances::free_balance(3), 90);
+
+ // release the license and get the bond back.
+ assert_ok!(CollatorSelection::release_license(RuntimeOrigin::signed(3)));
+ assert_eq!(Balances::free_balance(3), 100);
+
// register a candidate.
- assert_ok!(CollatorSelection::register_as_candidate(
- RuntimeOrigin::signed(3)
- ));
+ get_license_and_onboard(3);
assert_eq!(Balances::free_balance(3), 90);
- // register too so can leave above min candidates
- assert_ok!(CollatorSelection::register_as_candidate(
- RuntimeOrigin::signed(5)
- ));
- assert_eq!(Balances::free_balance(5), 90);
+ // can release license even if onboarded.
+ assert_ok!(CollatorSelection::release_license(RuntimeOrigin::signed(3)));
+ assert_eq!(Balances::free_balance(3), 100);
+ assert_eq!(CollatorSelection::candidates(), vec![]);
+ });
+}
+
+#[test]
+fn force_revoke_license() {
+ new_test_ext().execute_with(|| {
+ // obtain a license to collate and reserve the bond.
+ assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(3)));
+ assert_eq!(Balances::free_balance(3), 90);
- // cannot leave if not candidate.
+ // cannot execute the operation as non-root
assert_noop!(
- CollatorSelection::leave_intent(RuntimeOrigin::signed(4)),
- Error::<Test>::NotCandidate
+ CollatorSelection::force_revoke_license(RuntimeOrigin::signed(3), 3),
+ BadOrigin
);
- // bond is returned
- assert_ok!(CollatorSelection::leave_intent(RuntimeOrigin::signed(3)));
+ // release the license and get the bond back.
+ assert_ok!(CollatorSelection::force_revoke_license(
+ RuntimeOrigin::signed(RootAccount::get()),
+ 3
+ ));
+ assert_eq!(Balances::free_balance(3), 100);
+
+ // register a candidate.
+ get_license_and_onboard(3);
+ assert_eq!(Balances::free_balance(3), 90);
+
+ // can release license even if onboarded.
+ assert_ok!(CollatorSelection::force_revoke_license(
+ RuntimeOrigin::signed(RootAccount::get()),
+ 3
+ ));
assert_eq!(Balances::free_balance(3), 100);
- assert_eq!(CollatorSelection::last_authored_block(3), 0);
+ assert_eq!(CollatorSelection::candidates(), vec![]);
});
}
@@ -325,18 +415,11 @@
// 4 is the default author.
assert_eq!(Balances::free_balance(4), 100);
- assert_ok!(CollatorSelection::register_as_candidate(
- RuntimeOrigin::signed(4)
- ));
+ get_license_and_onboard(4);
// triggers `note_author`
Authorship::on_initialize(1);
- let collator = LicenseInfo {
- who: 4,
- deposit: 10,
- };
-
- assert_eq!(CollatorSelection::candidates(), vec![collator]);
+ assert_eq!(CollatorSelection::candidates(), vec![4]);
assert_eq!(CollatorSelection::last_authored_block(4), 0);
// half of the pot goes to the collator who's the author (4 in tests).
@@ -355,18 +438,11 @@
Balances::make_free_balance_be(&CollatorSelection::account_id(), 5);
// 4 is the default author.
assert_eq!(Balances::free_balance(4), 100);
- assert_ok!(CollatorSelection::register_as_candidate(
- RuntimeOrigin::signed(4)
- ));
+ get_license_and_onboard(4);
// triggers `note_author`
Authorship::on_initialize(1);
- let collator = LicenseInfo {
- who: 4,
- deposit: 10,
- };
-
- assert_eq!(CollatorSelection::candidates(), vec![collator]);
+ assert_eq!(CollatorSelection::candidates(), vec![4]);
assert_eq!(CollatorSelection::last_authored_block(4), 0);
// Nothing received
assert_eq!(Balances::free_balance(4), 90);
@@ -389,9 +465,7 @@
assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
// add a new collator
- assert_ok!(CollatorSelection::register_as_candidate(
- RuntimeOrigin::signed(3)
- ));
+ get_license_and_onboard(5);
// session won't see this.
assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
@@ -410,7 +484,7 @@
initialize_to_block(20);
assert_eq!(SessionChangeBlock::get(), 20);
// changed are now reflected to session handlers.
- assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 3]);
+ assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 5]);
});
}
@@ -418,64 +492,28 @@
fn kick_mechanism() {
new_test_ext().execute_with(|| {
// add a new collator
- assert_ok!(CollatorSelection::register_as_candidate(
- RuntimeOrigin::signed(3)
- ));
- assert_ok!(CollatorSelection::register_as_candidate(
- RuntimeOrigin::signed(4)
- ));
+ get_license_and_onboard(3);
+ get_license_and_onboard(4);
+
initialize_to_block(10);
assert_eq!(CollatorSelection::candidates().len(), 2);
+
initialize_to_block(20);
assert_eq!(SessionChangeBlock::get(), 20);
// 4 authored this block, gets to stay 3 was kicked
assert_eq!(CollatorSelection::candidates().len(), 1);
// 3 will be kicked after 1 session delay
assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 3, 4]);
- let collator = LicenseInfo {
- who: 4,
- deposit: 10,
- };
- assert_eq!(CollatorSelection::candidates(), vec![collator]);
- assert_eq!(CollatorSelection::kick_threshold(), 1);
+
+ assert_eq!(CollatorSelection::candidates(), vec![4]);
+ assert_eq!(CollatorSelection::kick_threshold(), 10);
assert_eq!(CollatorSelection::last_authored_block(4), 20);
+
initialize_to_block(30);
// 3 gets kicked after 1 session delay
assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 4]);
- // kicked collator gets funds back
- assert_eq!(Balances::free_balance(3), 100);
- });
-}
-
-#[test]
-fn should_not_kick_mechanism_too_few() {
- new_test_ext().execute_with(|| {
- // add a new collator
- assert_ok!(CollatorSelection::register_as_candidate(
- RuntimeOrigin::signed(3)
- ));
- assert_ok!(CollatorSelection::register_as_candidate(
- RuntimeOrigin::signed(5)
- ));
- initialize_to_block(10);
- assert_eq!(CollatorSelection::candidates().len(), 2);
- initialize_to_block(20);
- assert_eq!(SessionChangeBlock::get(), 20);
- // 4 authored this block, 5 gets to stay too few 3 was kicked
- assert_eq!(CollatorSelection::candidates().len(), 1);
- // 3 will be kicked after 1 session delay
- assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 3, 5]);
- let collator = LicenseInfo {
- who: 5,
- deposit: 10,
- };
- assert_eq!(CollatorSelection::candidates(), vec![collator]);
- assert_eq!(CollatorSelection::last_authored_block(4), 20);
- initialize_to_block(30);
- // 3 gets kicked after 1 session delay
- assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 5]);
- // kicked collator gets funds back
- assert_eq!(Balances::free_balance(3), 100);
+ // kicked collator gets their funds slashed, the deposit going to treasury
+ assert_eq!(Balances::free_balance(3), 90);
});
}
@@ -489,9 +527,9 @@
let invulnerables = vec![1, 1];
let collator_selection = collator_selection::GenesisConfig::<Test> {
- desired_candidates: 2,
+ desired_collators: 5,
license_bond: 10,
- kick_threshold: 1,
+ kick_threshold: 10,
invulnerables,
};
// collator selection must be initialized before session.
pallets/collator-selection/src/weights.rsdiffbeforeafterboth--- a/pallets/collator-selection/src/weights.rs
+++ b/pallets/collator-selection/src/weights.rs
@@ -45,7 +45,7 @@
// The weight info trait for `pallet_collator_selection`.
pub trait WeightInfo {
fn set_invulnerables(_b: u32) -> Weight;
- fn set_desired_candidates() -> Weight;
+ fn set_desired_collators() -> Weight;
fn set_license_bond() -> Weight;
fn register_as_candidate(_c: u32) -> Weight;
fn leave_intent(_c: u32) -> Weight;
@@ -62,7 +62,7 @@
.saturating_add(Weight::from_ref_time(68_000 as u64).saturating_mul(b as u64))
.saturating_add(T::DbWeight::get().writes(1 as u64))
}
- fn set_desired_candidates() -> Weight {
+ fn set_desired_collators() -> Weight {
Weight::from_ref_time(16_363_000 as u64).saturating_add(T::DbWeight::get().writes(1 as u64))
}
fn set_license_bond() -> Weight {
@@ -108,7 +108,7 @@
.saturating_add(Weight::from_ref_time(68_000 as u64).saturating_mul(b as u64))
.saturating_add(RocksDbWeight::get().writes(1 as u64))
}
- fn set_desired_candidates() -> Weight {
+ fn set_desired_collators() -> Weight {
Weight::from_ref_time(16_363_000 as u64)
.saturating_add(RocksDbWeight::get().writes(1 as u64))
}
primitives/common/src/constants.rsdiffbeforeafterboth--- a/primitives/common/src/constants.rs
+++ b/primitives/common/src/constants.rs
@@ -45,9 +45,9 @@
/// Minimum balance required to create or keep an account open.
pub const EXISTENTIAL_DEPOSIT: u128 = 0;
/// Amount of Balance reserved for candidate registration.
-pub const GENESIS_LICENSE_BOND: u128 = EXISTENTIAL_DEPOSIT;
+pub const GENESIS_LICENSE_BOND: u128 = 1_000_000_000_000 * UNIQUE;
/// How long a periodic session lasts in blocks.
-pub const SESSION_LENGTH: BlockNumber = MINUTES;
+pub const SESSION_LENGTH: BlockNumber = HOURS;
// Targeting 0.1 UNQ per transfer
pub const WEIGHT_TO_FEE_COEFF: u32 = /*<weight2fee>*/175_199_920/*</weight2fee>*/;
runtime/common/config/pallets/collator_selection.rsdiffbeforeafterboth--- a/runtime/common/config/pallets/collator_selection.rs
+++ b/runtime/common/config/pallets/collator_selection.rs
@@ -55,9 +55,7 @@
parameter_types! {
pub const PotId: PalletId = PalletId(*b"PotStake");
- pub const MaxCandidates: u32 = 30; // todo:collator 30 collator slots - 3 planned invulnerables
- pub const MinCandidates: u32 = 1;
- pub const MaxInvulnerables: u32 = 30;
+ pub const MaxCollators: u32 = 10;
pub const SlashRatio: Perbill = Perbill::from_percent(100);
}
@@ -68,9 +66,7 @@
type UpdateOrigin = EnsureRoot<AccountId>;
type TreasuryAccountId = TreasuryAccountId;
type PotId = PotId;
- type MaxCandidates = MaxCandidates;
- type MinCandidates = MinCandidates;
- type MaxInvulnerables = MaxInvulnerables;
+ type MaxCollators = MaxCollators;
// todo:collator kick threshold should be in storage and configured only by root -- or rather UpdateOrigin
type SlashRatio = SlashRatio;
type ValidatorId = <Self as frame_system::Config>::AccountId;
runtime/common/mod.rsdiffbeforeafterboth--- a/runtime/common/mod.rs
+++ b/runtime/common/mod.rs
@@ -192,7 +192,7 @@
};
use pallet_session::SessionManager;
use up_common::constants::GENESIS_LICENSE_BOND;
- use crate::config::pallets::collator_selection::MaxInvulnerables;
+ use crate::config::pallets::collator_selection::MaxCollators;
let mut weight = <Runtime as frame_system::Config>::DbWeight::get().reads(1);
@@ -231,17 +231,17 @@
})
.collect::<Vec<_>>();
- let bounded_invulnerables = BoundedVec::<_, MaxInvulnerables>::try_from(
+ let bounded_invulnerables = BoundedVec::<_, MaxCollators>::try_from(
invulnerables
.iter()
.cloned()
.map(|(acc, _)| acc)
.collect::<Vec<_>>(),
)
- .expect("Existing collators/invulnerables are more than MaxInvulnerables");
+ .expect("Existing collators/invulnerables are more than MaxCollators");
<pallet_collator_selection::Invulnerables<Runtime>>::put(bounded_invulnerables);
- <pallet_collator_selection::DesiredCandidates<Runtime>>::put(0);
+ <pallet_collator_selection::DesiredCollators<Runtime>>::put(MaxCollators::get());
<pallet_collator_selection::LicenseBond<Runtime>>::put(GENESIS_LICENSE_BOND);
let keys = invulnerables
runtime/common/tests/mod.rsdiffbeforeafterboth--- a/runtime/common/tests/mod.rs
+++ b/runtime/common/tests/mod.rs
@@ -18,7 +18,7 @@
use sp_core::{Public, Pair};
use sp_std::vec;
use up_common::types::AuraId;
-use crate::{GenesisConfig, ParachainInfoConfig, AuraConfig};
+use crate::{GenesisConfig, ParachainInfoConfig};
pub mod xcm;
@@ -28,7 +28,61 @@
.public()
}
+#[cfg(feature = "collator-selection")]
+fn new_test_ext(para_id: u32) -> sp_io::TestExternalities {
+ use sp_core::{sr25519};
+ use sp_runtime::traits::{IdentifyAccount, Verify};
+ use crate::{AccountId, Signature, SessionKeys, CollatorSelectionConfig, SessionConfig};
+
+ type AccountPublic = <Signature as Verify>::Signer;
+
+ fn get_account_id_from_seed<TPublic: Public>(seed: &str) -> AccountId
+ where
+ AccountPublic: From<<TPublic::Pair as Pair>::Public>,
+ {
+ AccountPublic::from(get_from_seed::<TPublic>(seed)).into_account()
+ }
+
+ let accounts = vec!["Alice", "Bob"];
+ let keys = accounts
+ .iter()
+ .map(|&acc| {
+ let account_id = get_account_id_from_seed::<sr25519::Public>(acc);
+ (
+ account_id.clone(),
+ account_id,
+ SessionKeys {
+ aura: get_from_seed::<AuraId>(acc),
+ },
+ )
+ })
+ .collect::<Vec<_>>();
+ let invulnerables = accounts
+ .iter()
+ .map(|acc| get_account_id_from_seed::<sr25519::Public>(acc))
+ .collect::<Vec<_>>();
+
+ let cfg = GenesisConfig {
+ collator_selection: CollatorSelectionConfig {
+ desired_collators: 2,
+ license_bond: 10,
+ kick_threshold: 10,
+ invulnerables,
+ },
+ session: SessionConfig { keys },
+ parachain_info: ParachainInfoConfig {
+ parachain_id: para_id.into(),
+ },
+ ..GenesisConfig::default()
+ };
+
+ cfg.build_storage().unwrap().into()
+}
+
+#[cfg(not(feature = "collator-selection"))]
fn new_test_ext(para_id: u32) -> sp_io::TestExternalities {
+ use crate::AuraConfig;
+
let cfg = GenesisConfig {
aura: AuraConfig {
authorities: vec![