difftreelog
feat(collator-selection) licenses + onboarding
in: master
8 files changed
node/cli/src/chain_spec.rsdiffbeforeafterboth24use serde_json::map::Map;24use serde_json::map::Map;252526use up_common::types::opaque::*;26use up_common::types::opaque::*;27use up_common::constants::{GENESIS_CANDIDACY_BOND, SESSION_LENGTH};27use up_common::constants::{GENESIS_LICENSE_BOND, SESSION_LENGTH};282829#[cfg(feature = "unique-runtime")]29#[cfg(feature = "unique-runtime")]30pub use unique_runtime as default_runtime;30pub use unique_runtime as default_runtime;196 .cloned()196 .cloned()197 .map(|(acc, _)| acc)197 .map(|(acc, _)| acc)198 .collect(),198 .collect(),199 candidacy_bond: GENESIS_CANDIDACY_BOND,199 license_bond: GENESIS_LICENSE_BOND,200 kick_threshold: SESSION_LENGTH,200 kick_threshold: SESSION_LENGTH,201 ..Default::default()201 ..Default::default()202 },202 },pallets/collator-selection/src/benchmarking.rsdiffbeforeafterboth116 .map(|c| account("candidate", c, SEED))116 .map(|c| account("candidate", c, SEED))117 .collect::<Vec<_>>();117 .collect::<Vec<_>>();118 assert!(118 assert!(119 <CandidacyBond<T>>::get() > 0u32.into(),119 <LicenseBond<T>>::get() > 0u32.into(),120 "Bond cannot be zero!"120 "Bond cannot be zero!"121 );121 );122122123 for who in candidates {123 for who in candidates {124 T::Currency::make_free_balance_be(&who, <CandidacyBond<T>>::get() * 2u32.into());124 T::Currency::make_free_balance_be(&who, <LicenseBond<T>>::get() * 2u32.into());125 <CollatorSelection<T>>::register_as_candidate(RawOrigin::Signed(who).into()).unwrap();125 <CollatorSelection<T>>::register_as_candidate(RawOrigin::Signed(who).into()).unwrap();126 }126 }127}127}154 assert_last_event::<T>(Event::NewDesiredCandidates{desired_candidates: max}.into());154 assert_last_event::<T>(Event::NewDesiredCandidates{desired_candidates: max}.into());155 }155 }156156157 set_candidacy_bond {157 set_license_bond {158 let bond_amount: BalanceOf<T> = T::Currency::minimum_balance() * 10u32.into();158 let bond_amount: BalanceOf<T> = T::Currency::minimum_balance() * 10u32.into();159 let origin = T::UpdateOrigin::successful_origin();159 let origin = T::UpdateOrigin::successful_origin();160 }: {160 }: {161 assert_ok!(161 assert_ok!(162 <CollatorSelection<T>>::set_candidacy_bond(origin, bond_amount.clone())162 <CollatorSelection<T>>::set_license_bond(origin, bond_amount.clone())163 );163 );164 }164 }165 verify {165 verify {166 assert_last_event::<T>(Event::NewCandidacyBond{bond_amount}.into());166 assert_last_event::<T>(Event::NewLicenseBond{bond_amount}.into());167 }167 }168168169 // worse case is when we have all the max-candidate slots filled except one, and we fill that169 // worse case is when we have all the max-candidate slots filled except one, and we fill that170 // one.170 // one.171 register_as_candidate {171 register_as_candidate {172 let c in 1 .. T::MaxCandidates::get();172 let c in 1 .. T::MaxCandidates::get();173173174 <CandidacyBond<T>>::put(T::Currency::minimum_balance());174 <LicenseBond<T>>::put(T::Currency::minimum_balance());175 <DesiredCandidates<T>>::put(c + 1);175 <DesiredCandidates<T>>::put(c + 1);176176177 register_validators::<T>(c);177 register_validators::<T>(c);195 // worse case is the last candidate leaving.195 // worse case is the last candidate leaving.196 leave_intent {196 leave_intent {197 let c in (T::MinCandidates::get() + 1) .. T::MaxCandidates::get();197 let c in (T::MinCandidates::get() + 1) .. T::MaxCandidates::get();198 <CandidacyBond<T>>::put(T::Currency::minimum_balance());198 <LicenseBond<T>>::put(T::Currency::minimum_balance());199 <DesiredCandidates<T>>::put(c);199 <DesiredCandidates<T>>::put(c);200200201 register_validators::<T>(c);201 register_validators::<T>(c);211211212 // worse case is paying a non-existing candidate account.212 // worse case is paying a non-existing candidate account.213 note_author {213 note_author {214 <CandidacyBond<T>>::put(T::Currency::minimum_balance());214 <LicenseBond<T>>::put(T::Currency::minimum_balance());215 T::Currency::make_free_balance_be(215 T::Currency::make_free_balance_be(216 &<CollatorSelection<T>>::account_id(),216 &<CollatorSelection<T>>::account_id(),217 T::Currency::minimum_balance() * 4u32.into(),217 T::Currency::minimum_balance() * 4u32.into(),233 let r in 1 .. T::MaxCandidates::get();233 let r in 1 .. T::MaxCandidates::get();234 let c in 1 .. T::MaxCandidates::get();234 let c in 1 .. T::MaxCandidates::get();235235236 <CandidacyBond<T>>::put(T::Currency::minimum_balance());236 <LicenseBond<T>>::put(T::Currency::minimum_balance());237 <DesiredCandidates<T>>::put(c);237 <DesiredCandidates<T>>::put(c);238 frame_system::Pallet::<T>::set_block_number(0u32.into());238 frame_system::Pallet::<T>::set_block_number(0u32.into());239239pallets/collator-selection/src/lib.rsdiffbeforeafterboth181 #[derive(181 #[derive(182 PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, scale_info::TypeInfo, MaxEncodedLen,182 PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, scale_info::TypeInfo, MaxEncodedLen,183 )]183 )]184 pub struct CandidateInfo<AccountId, Balance> {184 pub struct LicenseInfo<AccountId, Balance> {185 /// Account identifier.185 /// Account identifier.186 pub who: AccountId,186 pub who: AccountId,187 /// Reserved deposit.187 /// Reserved deposit.198 pub type Invulnerables<T: Config> =198 pub type Invulnerables<T: Config> =199 StorageValue<_, BoundedVec<T::AccountId, T::MaxInvulnerables>, ValueQuery>;199 StorageValue<_, BoundedVec<T::AccountId, T::MaxInvulnerables>, ValueQuery>;200201 /// The (community) collation license holders.202 #[pallet::storage]203 #[pallet::getter(fn licenses)]204 pub type Licenses<T: Config> =205 StorageMap<_, Blake2_128Concat, T::AccountId, BalanceOf<T>, ValueQuery>;200206201 /// The (community, limited) collation candidates.207 /// The (community, limited) collation candidates.202 #[pallet::storage]208 #[pallet::storage]203 #[pallet::getter(fn candidates)]209 #[pallet::getter(fn candidates)]204 pub type Candidates<T: Config> = StorageValue<210 pub type Candidates<T: Config> = StorageValue<205 _,211 _,206 BoundedVec<CandidateInfo<T::AccountId, BalanceOf<T>>, T::MaxCandidates>,212 BoundedVec<T::AccountId, T::MaxCandidates>, //LicenseInfo<T::AccountId, BalanceOf<T>>, T::MaxCandidates>, // license ID?207 ValueQuery,213 ValueQuery,208 >;214 >;209215231 ///237 ///232 /// When a collator calls `leave_intent` they immediately receive the deposit back.238 /// When a collator calls `leave_intent` they immediately receive the deposit back.233 #[pallet::storage]239 #[pallet::storage]234 #[pallet::getter(fn candidacy_bond)]240 #[pallet::getter(fn license_bond)]235 pub type CandidacyBond<T> = StorageValue<_, BalanceOf<T>, ValueQuery>;241 pub type LicenseBond<T> = StorageValue<_, BalanceOf<T>, ValueQuery>;236242237 #[pallet::genesis_config]243 #[pallet::genesis_config]238 pub struct GenesisConfig<T: Config> {244 pub struct GenesisConfig<T: Config> {239 pub invulnerables: Vec<T::AccountId>,245 pub invulnerables: Vec<T::AccountId>,240 pub candidacy_bond: BalanceOf<T>,246 pub license_bond: BalanceOf<T>,241 pub kick_threshold: T::BlockNumber,247 pub kick_threshold: T::BlockNumber,242 pub desired_candidates: u32,248 pub desired_candidates: u32,243 }249 }247 fn default() -> Self {253 fn default() -> Self {248 Self {254 Self {249 invulnerables: Default::default(),255 invulnerables: Default::default(),250 candidacy_bond: Default::default(),256 license_bond: Default::default(),251 kick_threshold: T::BlockNumber::one(),257 kick_threshold: T::BlockNumber::one(),252 desired_candidates: Default::default(),258 desired_candidates: Default::default(),253 }259 }275 );281 );276282277 <DesiredCandidates<T>>::put(&self.desired_candidates);283 <DesiredCandidates<T>>::put(&self.desired_candidates);278 <CandidacyBond<T>>::put(&self.candidacy_bond);284 <LicenseBond<T>>::put(&self.license_bond);279 <KickThreshold<T>>::put(&self.kick_threshold);285 <KickThreshold<T>>::put(&self.kick_threshold);280 <Invulnerables<T>>::put(bounded_invulnerables);286 <Invulnerables<T>>::put(bounded_invulnerables);281 }287 }287 NewDesiredCandidates {293 NewDesiredCandidates {288 desired_candidates: u32,294 desired_candidates: u32,289 },295 },290 NewCandidacyBond {296 NewLicenseBond {291 bond_amount: BalanceOf<T>,297 bond_amount: BalanceOf<T>,292 },298 },293 NewKickThreshold {299 NewKickThreshold {299 InvulnerableRemoved {305 InvulnerableRemoved {300 invulnerable: T::AccountId,306 invulnerable: T::AccountId,301 },307 },302 CandidateAdded {308 LicenseObtained {303 account_id: T::AccountId,309 account_id: T::AccountId,304 deposit: BalanceOf<T>,310 deposit: BalanceOf<T>,305 },311 },306 CandidateRemoved {312 LicenseForfeited {307 account_id: T::AccountId,313 account_id: T::AccountId,308 deposit_returned: BalanceOf<T>,314 deposit_returned: BalanceOf<T>,309 },315 },316 CandidateAdded {317 account_id: T::AccountId,318 },319 CandidateRemoved {320 account_id: T::AccountId,321 },310 }322 }311323312 // Errors inform users that something went wrong.324 // Errors inform users that something went wrong.320 Unknown,332 Unknown,321 /// Permission issue333 /// Permission issue322 Permission,334 Permission,335 /// User already holds license to collate336 AlreadyLicenseHolder,337 /// User does not hold a license to collate338 NoLicense,323 /// User is already a candidate339 /// User is already a candidate324 AlreadyCandidate,340 AlreadyCandidate,325 /// User is not a candidate341 /// User is not a candidate363 return Ok(().into());379 return Ok(().into());364 }380 }381382 // todo:collator check license holders, release moneys, promotion!383 // force_release_license? Error::<T>::lreadyLicenseHolder?365384366 <Invulnerables<T>>::try_append(new.clone())385 <Invulnerables<T>>::try_append(new.clone())367 .map_err(|_| Error::<T>::TooManyInvulnerables)?;386 .map_err(|_| Error::<T>::TooManyInvulnerables)?;419 }438 }420439421 /// Set the candidacy bond amount.440 /// Set the candidacy bond amount.422 #[pallet::weight(T::WeightInfo::set_candidacy_bond())]441 #[pallet::weight(T::WeightInfo::set_license_bond())]423 pub fn set_candidacy_bond(442 pub fn set_license_bond(424 origin: OriginFor<T>,443 origin: OriginFor<T>,425 bond: BalanceOf<T>,444 bond: BalanceOf<T>,426 ) -> DispatchResultWithPostInfo {445 ) -> DispatchResultWithPostInfo {427 T::UpdateOrigin::ensure_origin(origin)?;446 T::UpdateOrigin::ensure_origin(origin)?;428 <CandidacyBond<T>>::put(&bond);447 <LicenseBond<T>>::put(&bond);429 Self::deposit_event(Event::NewCandidacyBond { bond_amount: bond });448 Self::deposit_event(Event::NewLicenseBond { bond_amount: bond });430 Ok(().into())449 Ok(().into())431 }450 }432451433 /// Set the length of the kick threshold.452 /// Set the length of the kick threshold.434 /// Note that if the length is not a multiple of the session period, it might get inconsistent.453 /// Note that if the length is not a multiple of the session period, it might get inconsistent.435 #[pallet::weight(T::WeightInfo::set_candidacy_bond())] // todo:collator weight454 #[pallet::weight(T::WeightInfo::set_license_bond())] // todo:collator weight436 pub fn set_kick_threshold(455 pub fn set_kick_threshold(437 origin: OriginFor<T>,456 origin: OriginFor<T>,438 kick_threshold: T::BlockNumber,457 kick_threshold: T::BlockNumber,446 Ok(().into())465 Ok(().into())447 }466 }448467449 /// Register this account as a collator candidate. The account must (a) already have468 /// Purchase a license on block collation for this account.469 /// It does not make it a collator candidate, use `onboard` afterward. The account must450 /// registered session keys and (b) be able to reserve the `CandidacyBond`.470 /// (a) already have registered session keys and (b) be able to reserve the `LicenseBond`.451 ///471 ///452 /// This call is not available to `Invulnerable` collators.472 /// This call is not available to `Invulnerable` collators.453 #[pallet::weight(T::WeightInfo::register_as_candidate(T::MaxCandidates::get()))]473 #[pallet::weight(T::WeightInfo::register_as_candidate(T::MaxCandidates::get()))] // todo:collator weight454 pub fn register_as_candidate(origin: OriginFor<T>) -> DispatchResultWithPostInfo {474 pub fn get_license(origin: OriginFor<T>) -> DispatchResultWithPostInfo {475 // register_as_candidate476 let who = ensure_signed(origin)?;477478 if Licenses::<T>::contains_key(&who) {479 return Ok(().into());480 }481482 ensure!(483 !Self::invulnerables().contains(&who),484 Error::<T>::AlreadyInvulnerable485 );486487 let validator_key = T::ValidatorIdOf::convert(who.clone())488 .ok_or(Error::<T>::NoAssociatedValidatorId)?;489 ensure!(490 T::ValidatorRegistration::is_registered(&validator_key),491 Error::<T>::ValidatorNotRegistered492 );493494 let deposit = Self::license_bond();495 // First authored block is current block plus kick threshold to handle session delay496 /*let incoming = LicenseInfo {497 who: who.clone(),498 deposit,499 };*/500501 T::Currency::reserve(&who, deposit)?;502 Licenses::<T>::insert(who.clone(), deposit);503504 /*let current_count =505 <Licenses<T>>::try_mutate(|licenses| -> Result<usize, DispatchError> {506 if T::OriginPrivilegeCmp::cmp_privilege(&origin, &scheduled.origin) {507 return Err(BadOrigin.into());508 }509 if candidates.iter().any(|candidate| *candidate == who) {510 Err(Error::<T>::AlreadyLicenseHolder)?511 } else {512 T::Currency::reserve(&who, deposit)?;513 candidates514 .try_push(incoming)515 .map_err(|_| Error::<T>::TooManyCandidates)?;516 <LastAuthoredBlock<T>>::insert(517 who.clone(),518 frame_system::Pallet::<T>::block_number() + Self::kick_threshold(),519 );520 Ok(candidates.len())521 }522 })?;*/523524 Self::deposit_event(Event::LicenseObtained {525 account_id: who,526 deposit,527 });528 Ok(().into()) // Some(T::WeightInfo::register_as_candidate(current_count as u32)).into())529 }530531 /// Register this account as a candidate for collators for next sessions.532 /// The account must already hold a license, and cannot offboard immediately during a session.533 ///534 /// This call is not available to `Invulnerable` collators.535 #[pallet::weight(T::WeightInfo::register_as_candidate(T::MaxCandidates::get()))] // todo:collator weight536 pub fn onboard(origin: OriginFor<T>) -> DispatchResultWithPostInfo {537 // register_as_candidate455 let who = ensure_signed(origin)?;538 let who = ensure_signed(origin)?;456539540 // ensure the user obtained the license.541 ensure!(Licenses::<T>::contains_key(&who), Error::<T>::NoLicense);457 // ensure we are below limit.542 // ensure we are below limit.458 let length = <Candidates<T>>::decode_len().unwrap_or_default();543 let length = <Candidates<T>>::decode_len().unwrap_or_default();459 ensure!(544 ensure!(466 Error::<T>::AlreadyInvulnerable551 Error::<T>::AlreadyInvulnerable467 );552 );468469 let validator_key = T::ValidatorIdOf::convert(who.clone())470 .ok_or(Error::<T>::NoAssociatedValidatorId)?;471 ensure!(472 T::ValidatorRegistration::is_registered(&validator_key),473 Error::<T>::ValidatorNotRegistered474 );475553476 let deposit = Self::candidacy_bond();554 let deposit = Self::license_bond();477 // First authored block is current block plus kick threshold to handle session delay555 // First authored block is current block plus kick threshold to handle session delay478 let incoming = CandidateInfo {556 /*let incoming = LicenseInfo {479 who: who.clone(),557 who: who.clone(),480 deposit,558 deposit,481 };559 };*/482560483 let current_count =561 let current_count =484 <Candidates<T>>::try_mutate(|candidates| -> Result<usize, DispatchError> {562 <Candidates<T>>::try_mutate(|candidates| -> Result<usize, DispatchError> {485 if candidates.iter().any(|candidate| candidate.who == who) {563 if candidates.iter().any(|candidate| *candidate == who) {486 Err(Error::<T>::AlreadyCandidate)?564 Err(Error::<T>::AlreadyCandidate)?487 } else {565 } else {488 T::Currency::reserve(&who, deposit)?;566 T::Currency::reserve(&who, deposit)?;489 candidates567 candidates490 .try_push(incoming)568 .try_push(who.clone())491 .map_err(|_| Error::<T>::TooManyCandidates)?;569 .map_err(|_| Error::<T>::TooManyCandidates)?;492 <LastAuthoredBlock<T>>::insert(570 <LastAuthoredBlock<T>>::insert(493 who.clone(),571 who.clone(),498 })?;576 })?;499577500 Self::deposit_event(Event::CandidateAdded {578 Self::deposit_event(Event::CandidateAdded { account_id: who });501 account_id: who,502 deposit,503 });504 Ok(Some(T::WeightInfo::register_as_candidate(current_count as u32)).into())579 Ok(Some(T::WeightInfo::register_as_candidate(current_count as u32)).into())505 }580 }506581507 /// Deregister `origin` as a collator candidate. Note that the collator can only leave on582 /// Deregister `origin` as a collator candidate. Note that the collator can only leave on508 /// session change. The `CandidacyBond` will be unreserved immediately.583 /// session change. The license to `onboard` later at any other time will remain.509 ///584 ///510 /// This call will fail if the total number of candidates would drop below `MinCandidates`.585 /// This call will fail if the total number of candidates would drop below `MinCandidates`. todo:collator maybe not511 ///512 /// This call is not available to `Invulnerable` collators.513 #[pallet::weight(T::WeightInfo::leave_intent(T::MaxCandidates::get()))]586 #[pallet::weight(T::WeightInfo::leave_intent(T::MaxCandidates::get()))] // todo:collator weight514 pub fn leave_intent(origin: OriginFor<T>) -> DispatchResultWithPostInfo {587 pub fn offboard(origin: OriginFor<T>) -> DispatchResultWithPostInfo {588 // leave_intent515 let who = ensure_signed(origin)?;589 let who = ensure_signed(origin)?;516 // todo:collator invulnerables and candidates should count against min candidates together590 // todo:collator invulnerables and candidates should count against min candidates together517 ensure!(591 ensure!(518 Self::candidates().len() as u32 > T::MinCandidates::get(),592 Self::candidates().len() as u32 > T::MinCandidates::get(),519 Error::<T>::TooFewCandidates593 Error::<T>::TooFewCandidates520 );594 );521 let current_count = Self::try_remove_candidate(&who, false)?;595 let current_count = Self::try_remove_candidate(&who)?;522596523 Ok(Some(T::WeightInfo::leave_intent(current_count as u32)).into())597 Ok(Some(T::WeightInfo::leave_intent(current_count as u32)).into())524 }598 }599600 /// Forfeit `origin`'s own license. The `LicenseBond` will be unreserved immediately.601 ///602 /// This call is not available to `Invulnerable` collators.603 #[pallet::weight(T::WeightInfo::leave_intent(T::MaxCandidates::get()))] // todo:collator weight604 pub fn release_license(origin: OriginFor<T>) -> DispatchResultWithPostInfo {605 // leave_intent606 let who = ensure_signed(origin)?;607 // let current_count = Self::try_remove_candidate(&who, false)?;608 Self::try_release_license(&who, false)?;609610 Ok(().into())611 }612613 /// 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.615 /// The `LicenseBond` will be unreserved and returned immediately.616 ///617 /// This call is not available to `Invulnerable` collators.618 #[pallet::weight(T::WeightInfo::leave_intent(T::MaxCandidates::get()))] // todo:collator weight619 pub fn force_release_license(620 origin: OriginFor<T>,621 who: T::AccountId,622 ) -> DispatchResultWithPostInfo {623 // leave_intent624 T::UpdateOrigin::ensure_origin(origin)?;625626 let current_count = Self::try_remove_candidate(&who)?;627 Self::try_release_license(&who, false)?;628629 Ok(Some(T::WeightInfo::leave_intent(current_count as u32)).into()) // todo:collator weight630 }525 }631 }526632527 impl<T: Config> Pallet<T> {633 impl<T: Config> Pallet<T> {530 T::PotId::get().into_account_truncating()636 T::PotId::get().into_account_truncating()531 }637 }638639 /// 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> {641 let current_count =642 <Candidates<T>>::try_mutate(|candidates| -> Result<usize, DispatchError> {643 let index = candidates644 .iter()645 .position(|candidate| *candidate == *who)646 .ok_or(Error::<T>::NotCandidate)?;647 candidates.remove(index);648 <LastAuthoredBlock<T>>::remove(who.clone());649 Ok(candidates.len())650 })?;651 Self::deposit_event(Event::CandidateRemoved {652 account_id: who.clone(),653 });654 Ok(current_count)655 }532656533 /// Removes a candidate if they exist and sends them back their deposit, optionally slashed.657 /// Removes a candidate if they exist and sends them back their deposit, optionally slashed.534 fn try_remove_candidate(658 fn try_release_license(who: &T::AccountId, should_slash: bool) -> DispatchResult {535 who: &T::AccountId,536 should_slash: bool,537 ) -> Result<usize, DispatchError> {538 let mut deposit_returned = BalanceOf::<T>::default();659 let mut deposit_returned = BalanceOf::<T>::default();539 let current_count =540 <Candidates<T>>::try_mutate(|candidates| -> Result<usize, DispatchError> {660 Licenses::<T>::try_mutate_exists(&who, |deposit| -> DispatchResult {541 let index = candidates542 .iter()543 .position(|candidate| candidate.who == *who)544 .ok_or(Error::<T>::NotCandidate)?;545 let candidate = candidates.remove(index);661 if let Some(deposit) = deposit.take() {546 let deposit = candidate.deposit;547548 if should_slash {662 if should_slash {549 let slashed = T::SlashRatio::get() * deposit;663 let slashed = T::SlashRatio::get() * deposit;555669556 T::Currency::resolve_creating(&T::TreasuryAccountId::get(), imbalance);670 T::Currency::resolve_creating(&T::TreasuryAccountId::get(), imbalance);557558 // Self::deposit_event(Event::CandidateSlashed(who.clone()));559 } else {671 } else {560 //T::Currency::unreserve(who, deposit);672 //T::Currency::unreserve(who, deposit);561 deposit_returned = deposit;673 deposit_returned = deposit;562 }674 }563675564 T::Currency::unreserve(who, deposit_returned);676 T::Currency::unreserve(who, deposit_returned);565 // candidates.remove(index);566 <LastAuthoredBlock<T>>::remove(who.clone());677 Ok(())678 } else {567 Ok(candidates.len())679 Err(Error::<T>::NoLicense.into())680 }568 })?;681 })?;569 Self::deposit_event(Event::CandidateRemoved {682 Self::deposit_event(Event::LicenseForfeited {570 account_id: who.clone(),683 account_id: who.clone(),571 deposit_returned,684 deposit_returned,572 });685 });573 Ok(current_count)686 Ok(())574 }687 }575688576 /// Assemble the current set of candidates and invulnerables into the next collator set.689 /// Assemble the current set of candidates and invulnerables into the next collator set.587 /// Kicks out candidates that did not produce a block in the kick threshold700 /// Kicks out candidates that did not produce a block in the kick threshold588 /// and **confiscates** their deposits to the treasury.701 /// and **confiscates** their deposits to the treasury.589 pub fn kick_stale_candidates(702 pub fn kick_stale_candidates(590 candidates: BoundedVec<CandidateInfo<T::AccountId, BalanceOf<T>>, T::MaxCandidates>,703 candidates: BoundedVec<T::AccountId, T::MaxCandidates>, //LicenseInfo<T::AccountId, BalanceOf<T>>591 ) -> BoundedVec<T::AccountId, T::MaxCandidates> {704 ) -> BoundedVec<T::AccountId, T::MaxCandidates> {592 let now = frame_system::Pallet::<T>::block_number();705 let now = frame_system::Pallet::<T>::block_number();593 let kick_threshold = Self::kick_threshold();706 let kick_threshold = Self::kick_threshold();594 candidates707 candidates595 .into_iter()708 .into_iter()596 .filter_map(|c| {709 .filter_map(|c| {597 let last_block = <LastAuthoredBlock<T>>::get(c.who.clone());710 let last_block = <LastAuthoredBlock<T>>::get(c.clone());598 let since_last = now.saturating_sub(last_block);711 let since_last = now.saturating_sub(last_block);599 if since_last < kick_threshold ||712 if since_last < kick_threshold ||600 Self::candidates().len() as u32 <= T::MinCandidates::get()713 Self::candidates().len() as u32 <= T::MinCandidates::get()601 {714 {602 Some(c.who)715 Some(c)603 } else {716 } else {604 let outcome = Self::try_remove_candidate(&c.who, true);717 let outcome = Self::try_remove_candidate(&c);605 if let Err(why) = outcome {718 if let Err(why) = outcome {606 log::warn!("Failed to remove candidate {:?}", why);719 log::warn!("Failed to remove candidate {:?}", why);607 debug_assert!(false, "failed to remove candidate {:?}", why);720 debug_assert!(false, "failed to remove candidate {:?}", why);721 return None;608 }722 }723 let outcome = Self::try_release_license(&c, true);724 if let Err(why) = outcome {725 log::warn!("Failed to release license {:?}", why);726 debug_assert!(false, "failed to release license {:?}", why);727 }609 None728 None610 }729 }611 })730 })pallets/collator-selection/src/mock.rsdiffbeforeafterboth264 .collect::<Vec<_>>();264 .collect::<Vec<_>>();265 let collator_selection = collator_selection::GenesisConfig::<Test> {265 let collator_selection = collator_selection::GenesisConfig::<Test> {266 desired_candidates: 2,266 desired_candidates: 2,267 candidacy_bond: 10,267 license_bond: 10,268 kick_threshold: 1,268 kick_threshold: 1,269 invulnerables,269 invulnerables,270 };270 };pallets/collator-selection/src/tests.rsdiffbeforeafterboth31// limitations under the License.31// limitations under the License.323233use crate as collator_selection;33use crate as collator_selection;34use crate::{mock::*, CandidateInfo, Error};34use crate::{mock::*, LicenseInfo, Error};35use frame_support::{35use frame_support::{36 assert_noop, assert_ok,36 assert_noop, assert_ok,37 traits::{Currency, GenesisBuild, OnInitialize},37 traits::{Currency, GenesisBuild, OnInitialize},43fn basic_setup_works() {43fn basic_setup_works() {44 new_test_ext().execute_with(|| {44 new_test_ext().execute_with(|| {45 assert_eq!(CollatorSelection::desired_candidates(), 2);45 assert_eq!(CollatorSelection::desired_candidates(), 2);46 assert_eq!(CollatorSelection::candidacy_bond(), 10);46 assert_eq!(CollatorSelection::license_bond(), 10);474748 assert!(CollatorSelection::candidates().is_empty());48 assert!(CollatorSelection::candidates().is_empty());49 assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);49 assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);133}133}134134135#[test]135#[test]136fn set_candidacy_bond() {136fn set_license_bond() {137 new_test_ext().execute_with(|| {137 new_test_ext().execute_with(|| {138 // given138 // given139 assert_eq!(CollatorSelection::candidacy_bond(), 10);139 assert_eq!(CollatorSelection::license_bond(), 10);140140141 // can set141 // can set142 assert_ok!(CollatorSelection::set_candidacy_bond(142 assert_ok!(CollatorSelection::set_license_bond(143 RuntimeOrigin::signed(RootAccount::get()),143 RuntimeOrigin::signed(RootAccount::get()),144 7144 7145 ));145 ));146 assert_eq!(CollatorSelection::candidacy_bond(), 7);146 assert_eq!(CollatorSelection::license_bond(), 7);147147148 // rejects bad origin.148 // rejects bad origin.149 assert_noop!(149 assert_noop!(150 CollatorSelection::set_candidacy_bond(RuntimeOrigin::signed(1), 8),150 CollatorSelection::set_license_bond(RuntimeOrigin::signed(1), 8),151 BadOrigin151 BadOrigin152 );152 );153 });153 });227 assert_ok!(CollatorSelection::register_as_candidate(227 assert_ok!(CollatorSelection::register_as_candidate(228 RuntimeOrigin::signed(3)228 RuntimeOrigin::signed(3)229 ));229 ));230 let addition = CandidateInfo {230 let addition = LicenseInfo {231 who: 3,231 who: 3,232 deposit: 10,232 deposit: 10,233 };233 };267 new_test_ext().execute_with(|| {267 new_test_ext().execute_with(|| {268 // given268 // given269 assert_eq!(CollatorSelection::desired_candidates(), 2);269 assert_eq!(CollatorSelection::desired_candidates(), 2);270 assert_eq!(CollatorSelection::candidacy_bond(), 10);270 assert_eq!(CollatorSelection::license_bond(), 10);271 assert_eq!(CollatorSelection::candidates(), Vec::new());271 assert_eq!(CollatorSelection::candidates(), Vec::new());272 assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);272 assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);273273331 // triggers `note_author`331 // triggers `note_author`332 Authorship::on_initialize(1);332 Authorship::on_initialize(1);333333334 let collator = CandidateInfo {334 let collator = LicenseInfo {335 who: 4,335 who: 4,336 deposit: 10,336 deposit: 10,337 };337 };361 // triggers `note_author`361 // triggers `note_author`362 Authorship::on_initialize(1);362 Authorship::on_initialize(1);363363364 let collator = CandidateInfo {364 let collator = LicenseInfo {365 who: 4,365 who: 4,366 deposit: 10,366 deposit: 10,367 };367 };432 assert_eq!(CollatorSelection::candidates().len(), 1);432 assert_eq!(CollatorSelection::candidates().len(), 1);433 // 3 will be kicked after 1 session delay433 // 3 will be kicked after 1 session delay434 assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 3, 4]);434 assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 3, 4]);435 let collator = CandidateInfo {435 let collator = LicenseInfo {436 who: 4,436 who: 4,437 deposit: 10,437 deposit: 10,438 };438 };465 assert_eq!(CollatorSelection::candidates().len(), 1);465 assert_eq!(CollatorSelection::candidates().len(), 1);466 // 3 will be kicked after 1 session delay466 // 3 will be kicked after 1 session delay467 assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 3, 5]);467 assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 3, 5]);468 let collator = CandidateInfo {468 let collator = LicenseInfo {469 who: 5,469 who: 5,470 deposit: 10,470 deposit: 10,471 };471 };490490491 let collator_selection = collator_selection::GenesisConfig::<Test> {491 let collator_selection = collator_selection::GenesisConfig::<Test> {492 desired_candidates: 2,492 desired_candidates: 2,493 candidacy_bond: 10,493 license_bond: 10,494 kick_threshold: 1,494 kick_threshold: 1,495 invulnerables,495 invulnerables,496 };496 };pallets/collator-selection/src/weights.rsdiffbeforeafterboth46pub trait WeightInfo {46pub trait WeightInfo {47 fn set_invulnerables(_b: u32) -> Weight;47 fn set_invulnerables(_b: u32) -> Weight;48 fn set_desired_candidates() -> Weight;48 fn set_desired_candidates() -> Weight;49 fn set_candidacy_bond() -> Weight;49 fn set_license_bond() -> Weight;50 fn register_as_candidate(_c: u32) -> Weight;50 fn register_as_candidate(_c: u32) -> Weight;51 fn leave_intent(_c: u32) -> Weight;51 fn leave_intent(_c: u32) -> Weight;52 fn note_author() -> Weight;52 fn note_author() -> Weight;65 fn set_desired_candidates() -> Weight {65 fn set_desired_candidates() -> Weight {66 Weight::from_ref_time(16_363_000 as u64).saturating_add(T::DbWeight::get().writes(1 as u64))66 Weight::from_ref_time(16_363_000 as u64).saturating_add(T::DbWeight::get().writes(1 as u64))67 }67 }68 fn set_candidacy_bond() -> Weight {68 fn set_license_bond() -> Weight {69 Weight::from_ref_time(16_840_000 as u64).saturating_add(T::DbWeight::get().writes(1 as u64))69 Weight::from_ref_time(16_840_000 as u64).saturating_add(T::DbWeight::get().writes(1 as u64))70 }70 }71 fn register_as_candidate(c: u32) -> Weight {71 fn register_as_candidate(c: u32) -> Weight {112 Weight::from_ref_time(16_363_000 as u64)112 Weight::from_ref_time(16_363_000 as u64)113 .saturating_add(RocksDbWeight::get().writes(1 as u64))113 .saturating_add(RocksDbWeight::get().writes(1 as u64))114 }114 }115 fn set_candidacy_bond() -> Weight {115 fn set_license_bond() -> Weight {116 Weight::from_ref_time(16_840_000 as u64)116 Weight::from_ref_time(16_840_000 as u64)117 .saturating_add(RocksDbWeight::get().writes(1 as u64))117 .saturating_add(RocksDbWeight::get().writes(1 as u64))118 }118 }primitives/common/src/constants.rsdiffbeforeafterboth45/// Minimum balance required to create or keep an account open.45/// Minimum balance required to create or keep an account open.46pub const EXISTENTIAL_DEPOSIT: u128 = 0;46pub const EXISTENTIAL_DEPOSIT: u128 = 0;47/// Amount of Balance reserved for candidate registration.47/// Amount of Balance reserved for candidate registration.48pub const GENESIS_CANDIDACY_BOND: u128 = EXISTENTIAL_DEPOSIT;48pub const GENESIS_LICENSE_BOND: u128 = EXISTENTIAL_DEPOSIT;49/// How long a periodic session lasts in blocks.49/// How long a periodic session lasts in blocks.50pub const SESSION_LENGTH: BlockNumber = HOURS;50pub const SESSION_LENGTH: BlockNumber = MINUTES;515152// Targeting 0.1 UNQ per transfer52// Targeting 0.1 UNQ per transfer53pub const WEIGHT_TO_FEE_COEFF: u32 = /*<weight2fee>*/175_199_920/*</weight2fee>*/;53pub const WEIGHT_TO_FEE_COEFF: u32 = /*<weight2fee>*/175_199_920/*</weight2fee>*/;runtime/common/mod.rsdiffbeforeafterboth191 RuntimeAppPublic,191 RuntimeAppPublic,192 };192 };193 use pallet_session::SessionManager;193 use pallet_session::SessionManager;194 use up_common::constants::GENESIS_CANDIDACY_BOND;194 use up_common::constants::GENESIS_LICENSE_BOND;195 use crate::config::pallets::collator_selection::MaxInvulnerables;195 use crate::config::pallets::collator_selection::MaxInvulnerables;196196197 let mut weight = <Runtime as frame_system::Config>::DbWeight::get().reads(1);197 let mut weight = <Runtime as frame_system::Config>::DbWeight::get().reads(1);242242243 <pallet_collator_selection::Invulnerables<Runtime>>::put(bounded_invulnerables);243 <pallet_collator_selection::Invulnerables<Runtime>>::put(bounded_invulnerables);244 <pallet_collator_selection::DesiredCandidates<Runtime>>::put(0);244 <pallet_collator_selection::DesiredCandidates<Runtime>>::put(0);245 <pallet_collator_selection::CandidacyBond<Runtime>>::put(GENESIS_CANDIDACY_BOND);245 <pallet_collator_selection::LicenseBond<Runtime>>::put(GENESIS_LICENSE_BOND);246246247 let keys = invulnerables247 let keys = invulnerables248 .into_iter()248 .into_iter()