Better Handling of Candidates Who Become Invulnerable (#2801)

* remove candidate when to invulnerable

* fix

* candidates to collators

* make parameters consistent and more reasonable

* add call to kick invulnerable candidates

* factor removal into weight

* fix: use accrue instead of add

* make set_invulnerables non-atomic

* benchmark add_invulnerable to account for candidate removal

* don't remove from candidates with set_invulnerables

* fix bounds on benchmarking

* protect against zero min invulnerables underflow

* extra event and tests

* make candidates/invulnerables self-cleaning on session change

* add integrity test

* unused imports

* make rococo-contracts have 1 collator
This commit is contained in:
joe petrowski
2023-07-07 13:18:27 +02:00
committed by GitHub
parent 8f75f13e5a
commit 5a8134029a
23 changed files with 594 additions and 261 deletions
@@ -25,7 +25,8 @@ use frame_benchmarking::{
use frame_support::{
assert_ok,
codec::Decode,
traits::{Currency, EnsureOrigin, Get},
dispatch::DispatchResult,
traits::{Currency, EnsureOrigin, Get, ReservableCurrency},
};
use frame_system::{EventRecord, RawOrigin};
use pallet_authorship::EventHandler;
@@ -106,6 +107,18 @@ fn register_candidates<T: Config>(count: u32) {
}
}
fn min_candidates<T: Config>() -> u32 {
let min_collators = T::MinEligibleCollators::get();
let invulnerable_length = <Invulnerables<T>>::get().len();
min_collators.saturating_sub(invulnerable_length.try_into().unwrap())
}
fn min_invulnerables<T: Config>() -> u32 {
let min_collators = T::MinEligibleCollators::get();
let candidates_length = <Candidates<T>>::get().len();
min_collators.saturating_sub(candidates_length.try_into().unwrap())
}
benchmarks! {
where_clause { where T: pallet_authorship::Config + session::Config }
@@ -128,34 +141,67 @@ benchmarks! {
}
add_invulnerable {
let b in 1 .. T::MaxInvulnerables::get() - 1;
let c in 1 .. T::MaxCandidates::get() - 1;
let origin =
T::UpdateOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
// we're going to add one, so need one less than max set as invulnerables to start
let b in 1 .. T::MaxInvulnerables::get() - 1;
// need to fill up candidates
<CandidacyBond<T>>::put(T::Currency::minimum_balance());
<DesiredCandidates<T>>::put(c);
// get accounts and keys for the `c` candidates
let mut candidates = (0..c).map(|cc| validator::<T>(cc)).collect::<Vec<_>>();
// add one more to the list. should not be in `b` (invulnerables) because it's the account
// we will _add_ to invulnerables. we want it to be in `candidates` because we need the
// weight associated with removing it.
let (new_invulnerable, new_invulnerable_keys) = validator::<T>(b.max(c) + 1);
candidates.push((new_invulnerable.clone(), new_invulnerable_keys));
// set their keys ...
for (who, keys) in candidates.clone() {
<session::Pallet<T>>::set_keys(RawOrigin::Signed(who).into(), keys, Vec::new()).unwrap();
}
// ... and register them.
for (who, _) in candidates {
let deposit = <CandidacyBond<T>>::get();
T::Currency::make_free_balance_be(&who, deposit * 1000_u32.into());
let incoming = CandidateInfo { who: who.clone(), deposit };
<Candidates<T>>::try_mutate(|candidates| -> DispatchResult {
if !candidates.iter().any(|candidate| candidate.who == who) {
T::Currency::reserve(&who, deposit)?;
candidates.try_push(incoming).expect("we've respected the bounded vec limit");
<LastAuthoredBlock<T>>::insert(
who.clone(),
frame_system::Pallet::<T>::block_number() + T::KickThreshold::get(),
);
}
Ok(())
}).expect("only returns ok");
}
// now we need to fill up invulnerables
let mut invulnerables = register_validators::<T>(b);
invulnerables.sort();
let invulnerables: frame_support::BoundedVec<_, T::MaxInvulnerables> = frame_support::BoundedVec::try_from(invulnerables).unwrap();
let invulnerables: frame_support::BoundedVec<_, T::MaxInvulnerables> =
frame_support::BoundedVec::try_from(invulnerables).unwrap();
<Invulnerables<T>>::put(invulnerables);
// now let's set up a new one to add
let (new, keys) = validator::<T>(b + 1);
<session::Pallet<T>>::set_keys(RawOrigin::Signed(new.clone()).into(), keys, Vec::new()).unwrap();
}: {
assert_ok!(
<CollatorSelection<T>>::add_invulnerable(origin, new.clone())
<CollatorSelection<T>>::add_invulnerable(origin, new_invulnerable.clone())
);
}
verify {
assert_last_event::<T>(Event::InvulnerableAdded{account_id: new}.into());
assert_last_event::<T>(Event::InvulnerableAdded{account_id: new_invulnerable}.into());
}
remove_invulnerable {
let origin =
T::UpdateOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
let b in 1 .. T::MaxInvulnerables::get();
let b in (min_invulnerables::<T>() + 1) .. T::MaxInvulnerables::get();
let mut invulnerables = register_validators::<T>(b);
invulnerables.sort();
let invulnerables: frame_support::BoundedVec<_, T::MaxInvulnerables> = frame_support::BoundedVec::try_from(invulnerables).unwrap();
let invulnerables: frame_support::BoundedVec<_, T::MaxInvulnerables> =
frame_support::BoundedVec::try_from(invulnerables).unwrap();
<Invulnerables<T>>::put(invulnerables);
let to_remove = <Invulnerables<T>>::get().first().unwrap().clone();
}: {
@@ -221,7 +267,7 @@ benchmarks! {
// worse case is the last candidate leaving.
leave_intent {
let c in (T::MinCandidates::get() + 1) .. T::MaxCandidates::get();
let c in (min_candidates::<T>() + 1) .. T::MaxCandidates::get();
<CandidacyBond<T>>::put(T::Currency::minimum_balance());
<DesiredCandidates<T>>::put(c);
@@ -286,6 +332,7 @@ benchmarks! {
}
}
let min_candidates = min_candidates::<T>();
let pre_length = <Candidates<T>>::get().len();
frame_system::Pallet::<T>::set_block_number(new_block);
@@ -294,11 +341,19 @@ benchmarks! {
}: {
<CollatorSelection<T> as SessionManager<_>>::new_session(0)
} verify {
if c > r && non_removals >= T::MinCandidates::get() {
if c > r && non_removals >= min_candidates {
// candidates > removals and remaining candidates > min candidates
// => remaining candidates should be shorter than before removal, i.e. some were
// actually removed.
assert!(<Candidates<T>>::get().len() < pre_length);
} else if c > r && non_removals < T::MinCandidates::get() {
assert!(<Candidates<T>>::get().len() == T::MinCandidates::get() as usize);
} else if c > r && non_removals < min_candidates {
// candidates > removals and remaining candidates would be less than min candidates
// => remaining candidates should equal min candidates, i.e. some were removed up to
// the minimum, but then any more were "forced" to stay in candidates.
assert!(<Candidates<T>>::get().len() == min_candidates as usize);
} else {
// removals >= candidates, non removals must == 0
// can't remove more than exist
assert!(<Candidates<T>>::get().len() == pre_length);
}
}
+168 -55
View File
@@ -41,8 +41,9 @@
//! The current implementation resolves congestion of [`Candidates`] in a first-come-first-serve
//! manner.
//!
//! Candidates will not be allowed to get kicked or leave_intent if the total number of candidates
//! fall below MinCandidates. This is for potential disaster recovery scenarios.
//! Candidates will not be allowed to get kicked or `leave_intent` if the total number of collators
//! would fall below `MinEligibleCollators`. This is to ensure that some collators will always
//! exist, i.e. someone is eligible to produce a block.
//!
//! ### Rewards
//!
@@ -53,7 +54,7 @@
//! - Half the value of the transaction fees within the block. The other half of the transaction
//! fees are deposited into the Pot.
//!
//! To initiate rewards an ED needs to be transferred to the pot address.
//! To initiate rewards, an ED needs to be transferred to the pot address.
//!
//! Note: Eventually the Pot distribution may be modified as discussed in
//! [this issue](https://github.com/paritytech/statemint/issues/21#issuecomment-810481073).
@@ -128,17 +129,17 @@ pub mod pallet {
/// Account Identifier from which the internal Pot is generated.
type PotId: Get<PalletId>;
/// Maximum number of candidates that we should have. This is enforced in code.
/// Maximum number of candidates that we should have.
///
/// This does not take into account the invulnerables.
type MaxCandidates: Get<u32>;
/// Minimum number of candidates that we should have. This is used for disaster recovery.
///
/// This does not take into account the invulnerables.
type MinCandidates: Get<u32>;
/// Minimum number eligible collators. Should always be greater than zero. This includes
/// Invulnerable collators. This ensures that there will always be one collator who can
/// produce a block.
type MinEligibleCollators: Get<u32>;
/// Maximum number of invulnerables. This is enforced in code.
/// Maximum number of invulnerables.
type MaxInvulnerables: Get<u32>;
// Will be kicked if block is not produced in threshold.
@@ -180,7 +181,8 @@ pub mod pallet {
pub type Invulnerables<T: Config> =
StorageValue<_, BoundedVec<T::AccountId, T::MaxInvulnerables>, ValueQuery>;
/// The (community, limited) collation candidates.
/// The (community, limited) collation candidates. `Candidates` and `Invulnerables` should be
/// mutually exclusive.
#[pallet::storage]
#[pallet::getter(fn candidates)]
pub type Candidates<T: Config> = StorageValue<
@@ -262,6 +264,9 @@ pub mod pallet {
CandidateAdded { account_id: T::AccountId, deposit: BalanceOf<T> },
/// A candidate was removed.
CandidateRemoved { account_id: T::AccountId },
/// An account was unable to be added to the Invulnerables because they did not have keys
/// registered. Other Invulnerables may have been set.
InvalidInvulnerableSkipped { account_id: T::AccountId },
}
#[pallet::error]
@@ -269,7 +274,7 @@ pub mod pallet {
/// The pallet has too many candidates.
TooManyCandidates,
/// Leaving would result in too few candidates.
TooFewCandidates,
TooFewEligibleCollators,
/// Account is already a candidate.
AlreadyCandidate,
/// Account is not a candidate.
@@ -287,31 +292,81 @@ pub mod pallet {
}
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn integrity_test() {
assert!(T::MinEligibleCollators::get() > 0, "chain must require at least one collator");
}
}
#[pallet::call]
impl<T: Config> Pallet<T> {
/// Set the list of invulnerable (fixed) collators.
/// Set the list of invulnerable (fixed) collators. These collators must do some
/// preparation, namely to have registered session keys.
///
/// The call will remove any accounts that have not registered keys from the set. That is,
/// it is non-atomic; the caller accepts all `AccountId`s passed in `new` _individually_ as
/// acceptable Invulnerables, and is not proposing a _set_ of new Invulnerables.
///
/// This call does not maintain mutual exclusivity of `Invulnerables` and `Candidates`. It
/// is recommended to use a batch of `add_invulnerable` and `remove_invulnerable` instead.
/// A `batch_all` can also be used to enforce atomicity. If any candidates are included in
/// `new`, they should be removed with `remove_invulnerable_candidate` after execution.
///
/// Must be called by the `UpdateOrigin`.
#[pallet::call_index(0)]
#[pallet::weight(T::WeightInfo::set_invulnerables(new.len() as u32))]
pub fn set_invulnerables(
origin: OriginFor<T>,
new: Vec<T::AccountId>,
) -> DispatchResultWithPostInfo {
pub fn set_invulnerables(origin: OriginFor<T>, new: Vec<T::AccountId>) -> DispatchResult {
T::UpdateOrigin::ensure_origin(origin)?;
let mut bounded_invulnerables = BoundedVec::<_, T::MaxInvulnerables>::try_from(new)
.map_err(|_| Error::<T>::TooManyInvulnerables)?;
// check if the invulnerables have associated validator keys before they are set
for account_id in bounded_invulnerables.iter() {
let validator_key = T::ValidatorIdOf::convert(account_id.clone())
.ok_or(Error::<T>::NoAssociatedValidatorId)?;
// don't wipe out the collator set
if new.is_empty() {
ensure!(
T::ValidatorRegistration::is_registered(&validator_key),
Error::<T>::ValidatorNotRegistered
Self::candidates().len() as u32 >= T::MinEligibleCollators::get(),
Error::<T>::TooFewEligibleCollators
);
}
// Will need to check the length again when putting into a bounded vec, but this
// prevents the iterator from having too many elements.
ensure!(
new.len() as u32 <= T::MaxInvulnerables::get(),
Error::<T>::TooManyInvulnerables
);
let mut new_with_keys = Vec::new();
// check if the invulnerables have associated validator keys before they are set
for account_id in &new {
// don't let one unprepared collator ruin things for everyone.
let validator_key = T::ValidatorIdOf::convert(account_id.clone());
match validator_key {
Some(key) => {
// key is not registered
if !T::ValidatorRegistration::is_registered(&key) {
Self::deposit_event(Event::InvalidInvulnerableSkipped {
account_id: account_id.clone(),
});
continue
}
// else condition passes; key is registered
},
// key does not exist
None => {
Self::deposit_event(Event::InvalidInvulnerableSkipped {
account_id: account_id.clone(),
});
continue
},
}
new_with_keys.push(account_id.clone());
}
// should never fail since `new_with_keys` must be equal to or shorter than `new`
let mut bounded_invulnerables =
BoundedVec::<_, T::MaxInvulnerables>::try_from(new_with_keys)
.map_err(|_| Error::<T>::TooManyInvulnerables)?;
// Invulnerables must be sorted for removal.
bounded_invulnerables.sort();
@@ -319,12 +374,15 @@ pub mod pallet {
Self::deposit_event(Event::NewInvulnerables {
invulnerables: bounded_invulnerables.to_vec(),
});
Ok(().into())
Ok(())
}
/// Set the ideal number of collators (not including the invulnerables).
/// 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.
/// Set the ideal number of non-invulnerable 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 candidates than the desired number.
///
/// The origin for this call must be the `UpdateOrigin`.
#[pallet::call_index(1)]
#[pallet::weight(T::WeightInfo::set_desired_candidates())]
pub fn set_desired_candidates(
@@ -342,6 +400,8 @@ pub mod pallet {
}
/// Set the candidacy bond amount.
///
/// The origin for this call must be the `UpdateOrigin`.
#[pallet::call_index(2)]
#[pallet::weight(T::WeightInfo::set_candidacy_bond())]
pub fn set_candidacy_bond(
@@ -401,28 +461,35 @@ pub mod pallet {
/// Deregister `origin` as a collator candidate. Note that the collator can only leave on
/// session change. The `CandidacyBond` will be unreserved immediately.
///
/// This call will fail if the total number of candidates would drop below `MinCandidates`.
///
/// This call is not available to `Invulnerable` collators.
/// This call will fail if the total number of candidates would drop below
/// `MinEligibleCollators`.
#[pallet::call_index(4)]
#[pallet::weight(T::WeightInfo::leave_intent(T::MaxCandidates::get()))]
pub fn leave_intent(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
let who = ensure_signed(origin)?;
ensure!(
Self::candidates().len() as u32 > T::MinCandidates::get(),
Error::<T>::TooFewCandidates
Self::eligible_collators() > T::MinEligibleCollators::get(),
Error::<T>::TooFewEligibleCollators
);
let current_count = Self::try_remove_candidate(&who)?;
// Do remove their last authored block.
let current_count = Self::try_remove_candidate(&who, true)?;
Ok(Some(T::WeightInfo::leave_intent(current_count as u32)).into())
}
/// Add a new account `who` to the list of `Invulnerables` collators.
/// Add a new account `who` to the list of `Invulnerables` collators. `who` must have
/// registered session keys. If `who` is a candidate, they will be removed.
///
/// The origin for this call must be the `UpdateOrigin`.
#[pallet::call_index(5)]
#[pallet::weight(T::WeightInfo::add_invulnerable(T::MaxInvulnerables::get() - 1))]
pub fn add_invulnerable(origin: OriginFor<T>, who: T::AccountId) -> DispatchResult {
#[pallet::weight(T::WeightInfo::add_invulnerable(
T::MaxInvulnerables::get().saturating_sub(1),
T::MaxCandidates::get()
))]
pub fn add_invulnerable(
origin: OriginFor<T>,
who: T::AccountId,
) -> DispatchResultWithPostInfo {
T::UpdateOrigin::ensure_origin(origin)?;
// ensure `who` has registered a validator key
@@ -443,8 +510,21 @@ pub mod pallet {
Ok(())
})?;
// Error just means `who` wasn't a candidate, which is the state we want anyway. Don't
// remove their last authored block, as they are still a collator.
let _ = Self::try_remove_candidate(&who, false);
Self::deposit_event(Event::InvulnerableAdded { account_id: who });
Ok(())
let weight_used = T::WeightInfo::add_invulnerable(
Self::invulnerables()
.len()
.try_into()
.unwrap_or(T::MaxInvulnerables::get().saturating_sub(1)),
Self::candidates().len().try_into().unwrap_or(T::MaxCandidates::get()),
);
Ok(Some(weight_used).into())
}
/// Remove an account `who` from the list of `Invulnerables` collators. `Invulnerables` must
@@ -456,6 +536,11 @@ pub mod pallet {
pub fn remove_invulnerable(origin: OriginFor<T>, who: T::AccountId) -> DispatchResult {
T::UpdateOrigin::ensure_origin(origin)?;
ensure!(
Self::eligible_collators() > T::MinEligibleCollators::get(),
Error::<T>::TooFewEligibleCollators
);
<Invulnerables<T>>::try_mutate(|invulnerables| -> DispatchResult {
let pos =
invulnerables.binary_search(&who).map_err(|_| Error::<T>::NotInvulnerable)?;
@@ -469,13 +554,29 @@ pub mod pallet {
}
impl<T: Config> Pallet<T> {
/// Get a unique, inaccessible account id from the `PotId`.
/// Get a unique, inaccessible account ID from the `PotId`.
pub fn account_id() -> T::AccountId {
T::PotId::get().into_account_truncating()
}
/// Removes a candidate if they exist and sends them back their deposit
fn try_remove_candidate(who: &T::AccountId) -> Result<usize, DispatchError> {
/// Return the total number of accounts that are eligible collators (candidates and
/// invulnerables).
fn eligible_collators() -> u32 {
Self::candidates()
.len()
.saturating_add(Self::invulnerables().len())
.try_into()
// More-or-less guaranteed not to Err since it's hard to imagine candidates +
// invulnerables being greater than (or for `usize` to be smaller than) `u32::MAX`,
// but return something "reasonable" instead of panicking.
.unwrap_or(T::MaxCandidates::get())
}
/// Removes a candidate if they exist and sends them back their deposit.
fn try_remove_candidate(
who: &T::AccountId,
remove_last_authored: bool,
) -> Result<usize, DispatchError> {
let current_count =
<Candidates<T>>::try_mutate(|candidates| -> Result<usize, DispatchError> {
let index = candidates
@@ -484,7 +585,9 @@ pub mod pallet {
.ok_or(Error::<T>::NotCandidate)?;
let candidate = candidates.remove(index);
T::Currency::unreserve(who, candidate.deposit);
<LastAuthoredBlock<T>>::remove(who.clone());
if remove_last_authored {
<LastAuthoredBlock<T>>::remove(who.clone())
};
Ok(candidates.len())
})?;
Self::deposit_event(Event::CandidateRemoved { account_id: who.clone() });
@@ -502,29 +605,39 @@ pub mod pallet {
collators
}
/// Kicks out candidates that did not produce a block in the kick threshold
/// and refund their deposits.
/// Kicks out candidates that did not produce a block in the kick threshold and refunds
/// their deposits.
pub fn kick_stale_candidates(
candidates: BoundedVec<CandidateInfo<T::AccountId, BalanceOf<T>>, T::MaxCandidates>,
) -> BoundedVec<T::AccountId, T::MaxCandidates> {
let now = frame_system::Pallet::<T>::block_number();
let kick_threshold = T::KickThreshold::get();
let min_collators = T::MinEligibleCollators::get();
candidates
.into_iter()
.filter_map(|c| {
let last_block = <LastAuthoredBlock<T>>::get(c.who.clone());
let since_last = now.saturating_sub(last_block);
if since_last < kick_threshold ||
Self::candidates().len() as u32 <= T::MinCandidates::get()
{
Some(c.who)
} else {
let outcome = Self::try_remove_candidate(&c.who);
if let Err(why) = outcome {
log::warn!("Failed to remove candidate {:?}", why);
debug_assert!(false, "failed to remove candidate {:?}", why);
}
let is_invulnerable = Self::invulnerables().contains(&c.who);
let is_lazy = since_last >= kick_threshold;
if is_invulnerable {
// They are invulnerable. No reason for them to be in Candidates also.
// We don't even care about the min collators here, because an Account
// should not be a collator twice.
let _ = Self::try_remove_candidate(&c.who, false);
None
} else {
if Self::eligible_collators() <= min_collators || !is_lazy {
// Either this is a good collator (not lazy) or we are at the minimum
// that the system needs. They get to stay.
Some(c.who)
} else {
// This collator has not produced a block recently enough. Bye bye.
let _ = Self::try_remove_candidate(&c.who, true);
None
}
}
})
.collect::<Vec<_>>()
@@ -188,9 +188,6 @@ ord_parameter_types! {
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 struct IsRegistered;
@@ -205,9 +202,9 @@ impl Config for Test {
type Currency = Balances;
type UpdateOrigin = EnsureSignedBy<RootAccount, u64>;
type PotId = PotId;
type MaxCandidates = MaxCandidates;
type MinCandidates = MinCandidates;
type MaxInvulnerables = MaxInvulnerables;
type MaxCandidates = ConstU32<20>;
type MinEligibleCollators = ConstU32<1>;
type MaxInvulnerables = ConstU32<20>;
type KickThreshold = Period;
type ValidatorId = <Self as frame_system::Config>::AccountId;
type ValidatorIdOf = IdentityCollator;
+128 -14
View File
@@ -50,16 +50,22 @@ fn it_should_set_invulnerables() {
CollatorSelection::set_invulnerables(RuntimeOrigin::signed(1), new_set),
BadOrigin
);
});
}
// cannot set invulnerables without associated validator keys
let invulnerables = vec![42];
assert_noop!(
CollatorSelection::set_invulnerables(
RuntimeOrigin::signed(RootAccount::get()),
invulnerables
),
Error::<Test>::ValidatorNotRegistered
);
#[test]
fn it_should_set_invulnerables_even_with_some_invalid() {
new_test_ext().execute_with(|| {
assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);
let new_with_invalid = vec![1, 4, 3, 42, 2];
assert_ok!(CollatorSelection::set_invulnerables(
RuntimeOrigin::signed(RootAccount::get()),
new_with_invalid
));
// should succeed and order them, but not include 42
assert_eq!(CollatorSelection::invulnerables(), vec![1, 2, 3, 4]);
});
}
@@ -190,6 +196,54 @@ fn remove_invulnerable_works() {
});
}
#[test]
fn candidate_to_invulnerable_works() {
new_test_ext().execute_with(|| {
initialize_to_block(1);
assert_eq!(CollatorSelection::desired_candidates(), 2);
assert_eq!(CollatorSelection::candidacy_bond(), 10);
assert_eq!(CollatorSelection::candidates(), Vec::new());
assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);
assert_eq!(Balances::free_balance(3), 100);
assert_eq!(Balances::free_balance(4), 100);
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
assert_eq!(Balances::free_balance(3), 90);
assert_eq!(Balances::free_balance(4), 90);
assert_ok!(CollatorSelection::add_invulnerable(
RuntimeOrigin::signed(RootAccount::get()),
3
));
System::assert_has_event(RuntimeEvent::CollatorSelection(crate::Event::CandidateRemoved {
account_id: 3,
}));
System::assert_has_event(RuntimeEvent::CollatorSelection(
crate::Event::InvulnerableAdded { account_id: 3 },
));
assert!(CollatorSelection::invulnerables().to_vec().contains(&3));
assert_eq!(Balances::free_balance(3), 100);
assert_eq!(CollatorSelection::candidates().len(), 1);
assert_ok!(CollatorSelection::add_invulnerable(
RuntimeOrigin::signed(RootAccount::get()),
4
));
System::assert_has_event(RuntimeEvent::CollatorSelection(crate::Event::CandidateRemoved {
account_id: 4,
}));
System::assert_has_event(RuntimeEvent::CollatorSelection(
crate::Event::InvulnerableAdded { account_id: 4 },
));
assert!(CollatorSelection::invulnerables().to_vec().contains(&4));
assert_eq!(Balances::free_balance(4), 100);
assert_eq!(CollatorSelection::candidates().len(), 0);
});
}
#[test]
fn set_desired_candidates_works() {
new_test_ext().execute_with(|| {
@@ -256,14 +310,31 @@ fn cannot_register_candidate_if_too_many() {
#[test]
fn cannot_unregister_candidate_if_too_few() {
new_test_ext().execute_with(|| {
assert_eq!(CollatorSelection::candidates(), Vec::new());
assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);
assert_ok!(CollatorSelection::remove_invulnerable(
RuntimeOrigin::signed(RootAccount::get()),
1
));
assert_noop!(
CollatorSelection::remove_invulnerable(RuntimeOrigin::signed(RootAccount::get()), 2),
Error::<Test>::TooFewEligibleCollators,
);
// reset desired candidates:
<crate::DesiredCandidates<Test>>::put(1);
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
// now we can remove `2`
assert_ok!(CollatorSelection::remove_invulnerable(
RuntimeOrigin::signed(RootAccount::get()),
2
));
// can not remove too few
assert_noop!(
CollatorSelection::leave_intent(RuntimeOrigin::signed(4)),
Error::<Test>::TooFewCandidates,
Error::<Test>::TooFewEligibleCollators,
);
})
}
@@ -487,28 +558,71 @@ fn kick_mechanism() {
#[test]
fn should_not_kick_mechanism_too_few() {
new_test_ext().execute_with(|| {
// add a new collator
// remove the invulnerables and add new collators 3 and 5
assert_eq!(CollatorSelection::candidates(), Vec::new());
assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);
assert_ok!(CollatorSelection::remove_invulnerable(
RuntimeOrigin::signed(RootAccount::get()),
1
));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
assert_ok!(CollatorSelection::remove_invulnerable(
RuntimeOrigin::signed(RootAccount::get()),
2
));
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
// 4 authored this block, 3 is kicked, 5 stays because of too few collators
assert_eq!(CollatorSelection::candidates().len(), 1);
// 3 will be kicked after 1 session delay
assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 3, 5]);
assert_eq!(SessionHandlerCollators::get(), vec![3, 5]);
let collator = CandidateInfo { 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]);
assert_eq!(SessionHandlerCollators::get(), vec![5]);
// kicked collator gets funds back
assert_eq!(Balances::free_balance(3), 100);
});
}
#[test]
fn should_kick_invulnerables_from_candidates_on_session_change() {
new_test_ext().execute_with(|| {
assert_eq!(CollatorSelection::candidates(), Vec::new());
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
assert_eq!(Balances::free_balance(3), 90);
assert_eq!(Balances::free_balance(4), 90);
assert_ok!(CollatorSelection::set_invulnerables(
RuntimeOrigin::signed(RootAccount::get()),
vec![1, 2, 3]
));
let collator_3 = CandidateInfo { who: 3, deposit: 10 };
let collator_4 = CandidateInfo { who: 4, deposit: 10 };
assert_eq!(CollatorSelection::candidates(), vec![collator_3, collator_4.clone()]);
assert_eq!(CollatorSelection::invulnerables(), vec![1, 2, 3]);
// session change
initialize_to_block(10);
// 3 is removed from candidates
assert_eq!(CollatorSelection::candidates(), vec![collator_4]);
// but not from invulnerables
assert_eq!(CollatorSelection::invulnerables(), vec![1, 2, 3]);
// and it got its deposit back
assert_eq!(Balances::free_balance(3), 100);
});
}
#[test]
#[should_panic = "duplicate invulnerables in genesis."]
fn cannot_set_genesis_value_twice() {
@@ -27,7 +27,7 @@ use sp_std::marker::PhantomData;
// The weight info trait for `pallet_collator_selection`.
pub trait WeightInfo {
fn set_invulnerables(_b: u32) -> Weight;
fn add_invulnerable(_b: u32) -> Weight;
fn add_invulnerable(_b: u32, _c: u32) -> Weight;
fn remove_invulnerable(_b: u32) -> Weight;
fn set_desired_candidates() -> Weight;
fn set_candidacy_bond() -> Weight;
@@ -82,23 +82,31 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
.saturating_add(T::DbWeight::get().writes(2_u64.saturating_mul(r as u64)))
.saturating_add(T::DbWeight::get().writes(2_u64.saturating_mul(c as u64)))
}
/// Storage: CollatorSelection Invulnerables (r:1 w:1)
/// Proof: CollatorSelection Invulnerables (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen)
/// Storage: Session NextKeys (r:1 w:0)
/// Proof Skipped: Session NextKeys (max_values: None, max_size: None, mode: Measured)
/// The range of component `b` is `[1, 99]`.
fn add_invulnerable(b: u32) -> Weight {
/// Storage: CollatorSelection Invulnerables (r:1 w:1)
/// Proof: CollatorSelection Invulnerables (max_values: Some(1), max_size: Some(641), added: 1136, mode: MaxEncodedLen)
/// Storage: CollatorSelection Candidates (r:1 w:1)
/// Proof: CollatorSelection Candidates (max_values: Some(1), max_size: Some(4802), added: 5297, mode: MaxEncodedLen)
/// Storage: System Account (r:1 w:1)
/// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen)
/// The range of component `b` is `[1, 19]`.
/// The range of component `c` is `[1, 99]`.
fn add_invulnerable(b: u32, c: u32) -> Weight {
// Proof Size summary in bytes:
// Measured: `581 + b * (37 ±0)`
// Estimated: `4687 + b * (37 ±0)`
// Minimum execution time: 269_126_000 picoseconds.
Weight::from_parts(286_711_880, 0)
.saturating_add(Weight::from_parts(0, 4687))
// Standard Error: 22_887
.saturating_add(Weight::from_parts(813_399, 0).saturating_mul(b.into()))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(1))
// Measured: `757 + b * (32 ±0) + c * (53 ±0)`
// Estimated: `6287 + b * (37 ±0) + c * (53 ±0)`
// Minimum execution time: 52_720_000 picoseconds.
Weight::from_parts(56_102_459, 0)
.saturating_add(Weight::from_parts(0, 6287))
// Standard Error: 12_957
.saturating_add(Weight::from_parts(26_422, 0).saturating_mul(b.into()))
// Standard Error: 2_456
.saturating_add(Weight::from_parts(128_528, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(3))
.saturating_add(Weight::from_parts(0, 37).saturating_mul(b.into()))
.saturating_add(Weight::from_parts(0, 53).saturating_mul(c.into()))
}
/// Storage: CollatorSelection Invulnerables (r:1 w:1)
/// Proof: CollatorSelection Invulnerables (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen)
@@ -161,23 +169,31 @@ impl WeightInfo for () {
.saturating_add(RocksDbWeight::get().writes(2_u64.saturating_mul(r as u64)))
.saturating_add(RocksDbWeight::get().writes(2_u64.saturating_mul(c as u64)))
}
/// Storage: CollatorSelection Invulnerables (r:1 w:1)
/// Proof: CollatorSelection Invulnerables (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen)
/// Storage: Session NextKeys (r:1 w:0)
/// Proof Skipped: Session NextKeys (max_values: None, max_size: None, mode: Measured)
/// The range of component `b` is `[1, 99]`.
fn add_invulnerable(b: u32) -> Weight {
/// Storage: CollatorSelection Invulnerables (r:1 w:1)
/// Proof: CollatorSelection Invulnerables (max_values: Some(1), max_size: Some(641), added: 1136, mode: MaxEncodedLen)
/// Storage: CollatorSelection Candidates (r:1 w:1)
/// Proof: CollatorSelection Candidates (max_values: Some(1), max_size: Some(4802), added: 5297, mode: MaxEncodedLen)
/// Storage: System Account (r:1 w:1)
/// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen)
/// The range of component `b` is `[1, 19]`.
/// The range of component `c` is `[1, 99]`.
fn add_invulnerable(b: u32, c: u32) -> Weight {
// Proof Size summary in bytes:
// Measured: `581 + b * (37 ±0)`
// Estimated: `4687 + b * (37 ±0)`
// Minimum execution time: 269_126_000 picoseconds.
Weight::from_parts(286_711_880, 0)
.saturating_add(Weight::from_parts(0, 4687))
// Standard Error: 22_887
.saturating_add(Weight::from_parts(813_399, 0).saturating_mul(b.into()))
.saturating_add(RocksDbWeight::get().reads(2))
.saturating_add(RocksDbWeight::get().writes(1))
// Measured: `757 + b * (32 ±0) + c * (53 ±0)`
// Estimated: `6287 + b * (37 ±0) + c * (53 ±0)`
// Minimum execution time: 52_720_000 picoseconds.
Weight::from_parts(56_102_459, 0)
.saturating_add(Weight::from_parts(0, 6287))
// Standard Error: 12_957
.saturating_add(Weight::from_parts(26_422, 0).saturating_mul(b.into()))
// Standard Error: 2_456
.saturating_add(Weight::from_parts(128_528, 0).saturating_mul(c.into()))
.saturating_add(RocksDbWeight::get().reads(4))
.saturating_add(RocksDbWeight::get().writes(3))
.saturating_add(Weight::from_parts(0, 37).saturating_mul(b.into()))
.saturating_add(Weight::from_parts(0, 53).saturating_mul(c.into()))
}
/// Storage: CollatorSelection Invulnerables (r:1 w:1)
/// Proof: CollatorSelection Invulnerables (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen)
@@ -433,10 +433,7 @@ impl pallet_aura::Config for Runtime {
parameter_types! {
pub const PotId: PalletId = PalletId(*b"PotStake");
pub const MaxCandidates: u32 = 1000;
pub const MinCandidates: u32 = 5;
pub const SessionLength: BlockNumber = 6 * HOURS;
pub const MaxInvulnerables: u32 = 100;
// StakingAdmin pluralistic body.
pub const StakingAdminBodyId: BodyId = BodyId::Defense;
}
@@ -452,9 +449,9 @@ impl pallet_collator_selection::Config for Runtime {
type Currency = Balances;
type UpdateOrigin = CollatorSelectionUpdateOrigin;
type PotId = PotId;
type MaxCandidates = MaxCandidates;
type MinCandidates = MinCandidates;
type MaxInvulnerables = MaxInvulnerables;
type MaxCandidates = ConstU32<100>;
type MinEligibleCollators = ConstU32<4>;
type MaxInvulnerables = ConstU32<20>;
// should be a multiple of session or things will get inconsistent
type KickThreshold = Period;
type ValidatorId = <Self as frame_system::Config>::AccountId;
+3 -6
View File
@@ -222,9 +222,6 @@ mod tests {
parameter_types! {
pub const PotId: PalletId = PalletId(*b"PotStake");
pub const MaxCandidates: u32 = 20;
pub const MaxInvulnerables: u32 = 20;
pub const MinCandidates: u32 = 1;
}
impl pallet_collator_selection::Config for Test {
@@ -232,9 +229,9 @@ mod tests {
type Currency = Balances;
type UpdateOrigin = EnsureRoot<AccountId>;
type PotId = PotId;
type MaxCandidates = MaxCandidates;
type MinCandidates = MinCandidates;
type MaxInvulnerables = MaxInvulnerables;
type MaxCandidates = ConstU32<20>;
type MinEligibleCollators = ConstU32<1>;
type MaxInvulnerables = ConstU32<20>;
type ValidatorId = <Self as frame_system::Config>::AccountId;
type ValidatorIdOf = IdentityCollator;
type ValidatorRegistration = IsRegistered;
@@ -598,10 +598,7 @@ impl pallet_aura::Config for Runtime {
parameter_types! {
pub const PotId: PalletId = PalletId(*b"PotStake");
pub const MaxCandidates: u32 = 1000;
pub const MinCandidates: u32 = 5;
pub const SessionLength: BlockNumber = 6 * HOURS;
pub const MaxInvulnerables: u32 = 100;
// StakingAdmin pluralistic body.
pub const StakingAdminBodyId: BodyId = BodyId::Defense;
}
@@ -617,9 +614,9 @@ impl pallet_collator_selection::Config for Runtime {
type Currency = Balances;
type UpdateOrigin = CollatorSelectionUpdateOrigin;
type PotId = PotId;
type MaxCandidates = MaxCandidates;
type MinCandidates = MinCandidates;
type MaxInvulnerables = MaxInvulnerables;
type MaxCandidates = ConstU32<100>;
type MinEligibleCollators = ConstU32<4>;
type MaxInvulnerables = ConstU32<20>;
// should be a multiple of session or things will get inconsistent
type KickThreshold = Period;
type ValidatorId = <Self as frame_system::Config>::AccountId;
@@ -147,23 +147,31 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
.saturating_add(T::DbWeight::get().reads(3))
.saturating_add(T::DbWeight::get().writes(4))
}
/// Storage: CollatorSelection Invulnerables (r:1 w:1)
/// Proof: CollatorSelection Invulnerables (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen)
/// Storage: Session NextKeys (r:1 w:0)
/// Proof Skipped: Session NextKeys (max_values: None, max_size: None, mode: Measured)
/// The range of component `b` is `[1, 99]`.
fn add_invulnerable(b: u32, ) -> Weight {
/// Storage: CollatorSelection Invulnerables (r:1 w:1)
/// Proof: CollatorSelection Invulnerables (max_values: Some(1), max_size: Some(641), added: 1136, mode: MaxEncodedLen)
/// Storage: CollatorSelection Candidates (r:1 w:1)
/// Proof: CollatorSelection Candidates (max_values: Some(1), max_size: Some(4802), added: 5297, mode: MaxEncodedLen)
/// Storage: System Account (r:1 w:1)
/// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen)
/// The range of component `b` is `[1, 19]`.
/// The range of component `c` is `[1, 99]`.
fn add_invulnerable(b: u32, c: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `581 + b * (37 ±0)`
// Estimated: `4687 + b * (37 ±0)`
// Minimum execution time: 269_126_000 picoseconds.
Weight::from_parts(286_711_880, 0)
.saturating_add(Weight::from_parts(0, 4687))
// Standard Error: 22_887
.saturating_add(Weight::from_parts(813_399, 0).saturating_mul(b.into()))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(1))
// Measured: `757 + b * (32 ±0) + c * (53 ±0)`
// Estimated: `6287 + b * (37 ±0) + c * (53 ±0)`
// Minimum execution time: 52_720_000 picoseconds.
Weight::from_parts(56_102_459, 0)
.saturating_add(Weight::from_parts(0, 6287))
// Standard Error: 12_957
.saturating_add(Weight::from_parts(26_422, 0).saturating_mul(b.into()))
// Standard Error: 2_456
.saturating_add(Weight::from_parts(128_528, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(3))
.saturating_add(Weight::from_parts(0, 37).saturating_mul(b.into()))
.saturating_add(Weight::from_parts(0, 53).saturating_mul(c.into()))
}
/// Storage: CollatorSelection Invulnerables (r:1 w:1)
/// Proof: CollatorSelection Invulnerables (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen)
@@ -611,10 +611,7 @@ impl pallet_aura::Config for Runtime {
parameter_types! {
pub const PotId: PalletId = PalletId(*b"PotStake");
pub const MaxCandidates: u32 = 1000;
pub const MinCandidates: u32 = 5;
pub const SessionLength: BlockNumber = 6 * HOURS;
pub const MaxInvulnerables: u32 = 100;
// `StakingAdmin` pluralistic body.
pub const StakingAdminBodyId: BodyId = BodyId::Defense;
}
@@ -630,9 +627,9 @@ impl pallet_collator_selection::Config for Runtime {
type Currency = Balances;
type UpdateOrigin = CollatorSelectionUpdateOrigin;
type PotId = PotId;
type MaxCandidates = MaxCandidates;
type MinCandidates = MinCandidates;
type MaxInvulnerables = MaxInvulnerables;
type MaxCandidates = ConstU32<100>;
type MinEligibleCollators = ConstU32<4>;
type MaxInvulnerables = ConstU32<20>;
// should be a multiple of session or things will get inconsistent
type KickThreshold = Period;
type ValidatorId = <Self as frame_system::Config>::AccountId;
@@ -147,23 +147,31 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
.saturating_add(T::DbWeight::get().reads(3))
.saturating_add(T::DbWeight::get().writes(4))
}
/// Storage: CollatorSelection Invulnerables (r:1 w:1)
/// Proof: CollatorSelection Invulnerables (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen)
/// Storage: Session NextKeys (r:1 w:0)
/// Proof Skipped: Session NextKeys (max_values: None, max_size: None, mode: Measured)
/// The range of component `b` is `[1, 99]`.
fn add_invulnerable(b: u32, ) -> Weight {
/// Storage: CollatorSelection Invulnerables (r:1 w:1)
/// Proof: CollatorSelection Invulnerables (max_values: Some(1), max_size: Some(641), added: 1136, mode: MaxEncodedLen)
/// Storage: CollatorSelection Candidates (r:1 w:1)
/// Proof: CollatorSelection Candidates (max_values: Some(1), max_size: Some(4802), added: 5297, mode: MaxEncodedLen)
/// Storage: System Account (r:1 w:1)
/// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen)
/// The range of component `b` is `[1, 19]`.
/// The range of component `c` is `[1, 99]`.
fn add_invulnerable(b: u32, c: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `581 + b * (37 ±0)`
// Estimated: `4687 + b * (37 ±0)`
// Minimum execution time: 269_126_000 picoseconds.
Weight::from_parts(286_711_880, 0)
.saturating_add(Weight::from_parts(0, 4687))
// Standard Error: 22_887
.saturating_add(Weight::from_parts(813_399, 0).saturating_mul(b.into()))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(1))
// Measured: `757 + b * (32 ±0) + c * (53 ±0)`
// Estimated: `6287 + b * (37 ±0) + c * (53 ±0)`
// Minimum execution time: 52_720_000 picoseconds.
Weight::from_parts(56_102_459, 0)
.saturating_add(Weight::from_parts(0, 6287))
// Standard Error: 12_957
.saturating_add(Weight::from_parts(26_422, 0).saturating_mul(b.into()))
// Standard Error: 2_456
.saturating_add(Weight::from_parts(128_528, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(3))
.saturating_add(Weight::from_parts(0, 37).saturating_mul(b.into()))
.saturating_add(Weight::from_parts(0, 53).saturating_mul(c.into()))
}
/// Storage: CollatorSelection Invulnerables (r:1 w:1)
/// Proof: CollatorSelection Invulnerables (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen)
@@ -637,10 +637,7 @@ impl pallet_aura::Config for Runtime {
parameter_types! {
pub const PotId: PalletId = PalletId(*b"PotStake");
pub const MaxCandidates: u32 = 1000;
pub const MinCandidates: u32 = 1;
pub const SessionLength: BlockNumber = 6 * HOURS;
pub const MaxInvulnerables: u32 = 100;
}
pub type CollatorSelectionUpdateOrigin = EnsureRoot<AccountId>;
@@ -650,9 +647,9 @@ impl pallet_collator_selection::Config for Runtime {
type Currency = Balances;
type UpdateOrigin = CollatorSelectionUpdateOrigin;
type PotId = PotId;
type MaxCandidates = MaxCandidates;
type MinCandidates = MinCandidates;
type MaxInvulnerables = MaxInvulnerables;
type MaxCandidates = ConstU32<100>;
type MinEligibleCollators = ConstU32<4>;
type MaxInvulnerables = ConstU32<20>;
// should be a multiple of session or things will get inconsistent
type KickThreshold = Period;
type ValidatorId = <Self as frame_system::Config>::AccountId;
@@ -147,23 +147,31 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
.saturating_add(T::DbWeight::get().reads(3))
.saturating_add(T::DbWeight::get().writes(4))
}
/// Storage: CollatorSelection Invulnerables (r:1 w:1)
/// Proof: CollatorSelection Invulnerables (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen)
/// Storage: Session NextKeys (r:1 w:0)
/// Proof Skipped: Session NextKeys (max_values: None, max_size: None, mode: Measured)
/// The range of component `b` is `[1, 99]`.
fn add_invulnerable(b: u32, ) -> Weight {
/// Storage: CollatorSelection Invulnerables (r:1 w:1)
/// Proof: CollatorSelection Invulnerables (max_values: Some(1), max_size: Some(641), added: 1136, mode: MaxEncodedLen)
/// Storage: CollatorSelection Candidates (r:1 w:1)
/// Proof: CollatorSelection Candidates (max_values: Some(1), max_size: Some(4802), added: 5297, mode: MaxEncodedLen)
/// Storage: System Account (r:1 w:1)
/// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen)
/// The range of component `b` is `[1, 19]`.
/// The range of component `c` is `[1, 99]`.
fn add_invulnerable(b: u32, c: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `581 + b * (37 ±0)`
// Estimated: `4687 + b * (37 ±0)`
// Minimum execution time: 269_126_000 picoseconds.
Weight::from_parts(286_711_880, 0)
.saturating_add(Weight::from_parts(0, 4687))
// Standard Error: 22_887
.saturating_add(Weight::from_parts(813_399, 0).saturating_mul(b.into()))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(1))
// Measured: `757 + b * (32 ±0) + c * (53 ±0)`
// Estimated: `6287 + b * (37 ±0) + c * (53 ±0)`
// Minimum execution time: 52_720_000 picoseconds.
Weight::from_parts(56_102_459, 0)
.saturating_add(Weight::from_parts(0, 6287))
// Standard Error: 12_957
.saturating_add(Weight::from_parts(26_422, 0).saturating_mul(b.into()))
// Standard Error: 2_456
.saturating_add(Weight::from_parts(128_528, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(3))
.saturating_add(Weight::from_parts(0, 37).saturating_mul(b.into()))
.saturating_add(Weight::from_parts(0, 53).saturating_mul(c.into()))
}
/// Storage: CollatorSelection Invulnerables (r:1 w:1)
/// Proof: CollatorSelection Invulnerables (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen)
@@ -352,7 +352,7 @@ parameter_types! {
pub const StakingAdminBodyId: BodyId = BodyId::Defense;
}
/// We allow root, the StakingAdmin to execute privileged collator selection operations.
/// We allow Root and the `StakingAdmin` to execute privileged collator selection operations.
pub type CollatorSelectionUpdateOrigin = EitherOfDiverse<
EnsureRoot<AccountId>,
EnsureXcm<IsVoiceOfBody<GovernanceLocation, StakingAdminBodyId>>,
@@ -363,9 +363,9 @@ impl pallet_collator_selection::Config for Runtime {
type Currency = Balances;
type UpdateOrigin = CollatorSelectionUpdateOrigin;
type PotId = PotId;
type MaxCandidates = ConstU32<1000>;
type MinCandidates = ConstU32<5>;
type MaxInvulnerables = ConstU32<100>;
type MaxCandidates = ConstU32<100>;
type MinEligibleCollators = ConstU32<4>;
type MaxInvulnerables = ConstU32<20>;
// should be a multiple of session or things will get inconsistent
type KickThreshold = ConstU32<PERIOD>;
type ValidatorId = <Self as frame_system::Config>::AccountId;
@@ -147,23 +147,31 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
.saturating_add(T::DbWeight::get().reads(3))
.saturating_add(T::DbWeight::get().writes(4))
}
/// Storage: CollatorSelection Invulnerables (r:1 w:1)
/// Proof: CollatorSelection Invulnerables (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen)
/// Storage: Session NextKeys (r:1 w:0)
/// Proof Skipped: Session NextKeys (max_values: None, max_size: None, mode: Measured)
/// The range of component `b` is `[1, 99]`.
fn add_invulnerable(b: u32, ) -> Weight {
/// Storage: CollatorSelection Invulnerables (r:1 w:1)
/// Proof: CollatorSelection Invulnerables (max_values: Some(1), max_size: Some(641), added: 1136, mode: MaxEncodedLen)
/// Storage: CollatorSelection Candidates (r:1 w:1)
/// Proof: CollatorSelection Candidates (max_values: Some(1), max_size: Some(4802), added: 5297, mode: MaxEncodedLen)
/// Storage: System Account (r:1 w:1)
/// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen)
/// The range of component `b` is `[1, 19]`.
/// The range of component `c` is `[1, 99]`.
fn add_invulnerable(b: u32, c: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `581 + b * (37 ±0)`
// Estimated: `4687 + b * (37 ±0)`
// Minimum execution time: 269_126_000 picoseconds.
Weight::from_parts(286_711_880, 0)
.saturating_add(Weight::from_parts(0, 4687))
// Standard Error: 22_887
.saturating_add(Weight::from_parts(813_399, 0).saturating_mul(b.into()))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(1))
// Measured: `757 + b * (32 ±0) + c * (53 ±0)`
// Estimated: `6287 + b * (37 ±0) + c * (53 ±0)`
// Minimum execution time: 52_720_000 picoseconds.
Weight::from_parts(56_102_459, 0)
.saturating_add(Weight::from_parts(0, 6287))
// Standard Error: 12_957
.saturating_add(Weight::from_parts(26_422, 0).saturating_mul(b.into()))
// Standard Error: 2_456
.saturating_add(Weight::from_parts(128_528, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(3))
.saturating_add(Weight::from_parts(0, 37).saturating_mul(b.into()))
.saturating_add(Weight::from_parts(0, 53).saturating_mul(c.into()))
}
/// Storage: CollatorSelection Invulnerables (r:1 w:1)
/// Proof: CollatorSelection Invulnerables (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen)
@@ -363,9 +363,9 @@ impl pallet_collator_selection::Config for Runtime {
type Currency = Balances;
type UpdateOrigin = CollatorSelectionUpdateOrigin;
type PotId = PotId;
type MaxCandidates = ConstU32<1000>;
type MinCandidates = ConstU32<5>;
type MaxInvulnerables = ConstU32<100>;
type MaxCandidates = ConstU32<100>;
type MinEligibleCollators = ConstU32<4>;
type MaxInvulnerables = ConstU32<20>;
// should be a multiple of session or things will get inconsistent
type KickThreshold = ConstU32<PERIOD>;
type ValidatorId = <Self as frame_system::Config>::AccountId;
@@ -147,23 +147,31 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
.saturating_add(T::DbWeight::get().reads(3))
.saturating_add(T::DbWeight::get().writes(4))
}
/// Storage: CollatorSelection Invulnerables (r:1 w:1)
/// Proof: CollatorSelection Invulnerables (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen)
/// Storage: Session NextKeys (r:1 w:0)
/// Proof Skipped: Session NextKeys (max_values: None, max_size: None, mode: Measured)
/// The range of component `b` is `[1, 99]`.
fn add_invulnerable(b: u32, ) -> Weight {
/// Storage: CollatorSelection Invulnerables (r:1 w:1)
/// Proof: CollatorSelection Invulnerables (max_values: Some(1), max_size: Some(641), added: 1136, mode: MaxEncodedLen)
/// Storage: CollatorSelection Candidates (r:1 w:1)
/// Proof: CollatorSelection Candidates (max_values: Some(1), max_size: Some(4802), added: 5297, mode: MaxEncodedLen)
/// Storage: System Account (r:1 w:1)
/// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen)
/// The range of component `b` is `[1, 19]`.
/// The range of component `c` is `[1, 99]`.
fn add_invulnerable(b: u32, c: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `581 + b * (37 ±0)`
// Estimated: `4687 + b * (37 ±0)`
// Minimum execution time: 269_126_000 picoseconds.
Weight::from_parts(286_711_880, 0)
.saturating_add(Weight::from_parts(0, 4687))
// Standard Error: 22_887
.saturating_add(Weight::from_parts(813_399, 0).saturating_mul(b.into()))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(1))
// Measured: `757 + b * (32 ±0) + c * (53 ±0)`
// Estimated: `6287 + b * (37 ±0) + c * (53 ±0)`
// Minimum execution time: 52_720_000 picoseconds.
Weight::from_parts(56_102_459, 0)
.saturating_add(Weight::from_parts(0, 6287))
// Standard Error: 12_957
.saturating_add(Weight::from_parts(26_422, 0).saturating_mul(b.into()))
// Standard Error: 2_456
.saturating_add(Weight::from_parts(128_528, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(3))
.saturating_add(Weight::from_parts(0, 37).saturating_mul(b.into()))
.saturating_add(Weight::from_parts(0, 53).saturating_mul(c.into()))
}
/// Storage: CollatorSelection Invulnerables (r:1 w:1)
/// Proof: CollatorSelection Invulnerables (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen)
@@ -361,9 +361,9 @@ impl pallet_collator_selection::Config for Runtime {
type Currency = Balances;
type UpdateOrigin = CollatorSelectionUpdateOrigin;
type PotId = PotId;
type MaxCandidates = ConstU32<1000>;
type MinCandidates = ConstU32<5>;
type MaxInvulnerables = ConstU32<100>;
type MaxCandidates = ConstU32<100>;
type MinEligibleCollators = ConstU32<4>;
type MaxInvulnerables = ConstU32<20>;
// should be a multiple of session or things will get inconsistent
type KickThreshold = ConstU32<PERIOD>;
type ValidatorId = <Self as frame_system::Config>::AccountId;
@@ -147,23 +147,31 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
.saturating_add(T::DbWeight::get().reads(3))
.saturating_add(T::DbWeight::get().writes(4))
}
/// Storage: CollatorSelection Invulnerables (r:1 w:1)
/// Proof: CollatorSelection Invulnerables (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen)
/// Storage: Session NextKeys (r:1 w:0)
/// Proof Skipped: Session NextKeys (max_values: None, max_size: None, mode: Measured)
/// The range of component `b` is `[1, 99]`.
fn add_invulnerable(b: u32, ) -> Weight {
/// Storage: CollatorSelection Invulnerables (r:1 w:1)
/// Proof: CollatorSelection Invulnerables (max_values: Some(1), max_size: Some(641), added: 1136, mode: MaxEncodedLen)
/// Storage: CollatorSelection Candidates (r:1 w:1)
/// Proof: CollatorSelection Candidates (max_values: Some(1), max_size: Some(4802), added: 5297, mode: MaxEncodedLen)
/// Storage: System Account (r:1 w:1)
/// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen)
/// The range of component `b` is `[1, 19]`.
/// The range of component `c` is `[1, 99]`.
fn add_invulnerable(b: u32, c: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `581 + b * (37 ±0)`
// Estimated: `4687 + b * (37 ±0)`
// Minimum execution time: 269_126_000 picoseconds.
Weight::from_parts(286_711_880, 0)
.saturating_add(Weight::from_parts(0, 4687))
// Standard Error: 22_887
.saturating_add(Weight::from_parts(813_399, 0).saturating_mul(b.into()))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(1))
// Measured: `757 + b * (32 ±0) + c * (53 ±0)`
// Estimated: `6287 + b * (37 ±0) + c * (53 ±0)`
// Minimum execution time: 52_720_000 picoseconds.
Weight::from_parts(56_102_459, 0)
.saturating_add(Weight::from_parts(0, 6287))
// Standard Error: 12_957
.saturating_add(Weight::from_parts(26_422, 0).saturating_mul(b.into()))
// Standard Error: 2_456
.saturating_add(Weight::from_parts(128_528, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(3))
.saturating_add(Weight::from_parts(0, 37).saturating_mul(b.into()))
.saturating_add(Weight::from_parts(0, 53).saturating_mul(c.into()))
}
/// Storage: CollatorSelection Invulnerables (r:1 w:1)
/// Proof: CollatorSelection Invulnerables (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen)
@@ -436,9 +436,9 @@ impl pallet_collator_selection::Config for Runtime {
type Currency = Balances;
type UpdateOrigin = CollatorSelectionUpdateOrigin;
type PotId = PotId;
type MaxCandidates = ConstU32<1000>;
type MinCandidates = ConstU32<5>;
type MaxInvulnerables = ConstU32<100>;
type MaxCandidates = ConstU32<100>;
type MinEligibleCollators = ConstU32<4>;
type MaxInvulnerables = ConstU32<20>;
// should be a multiple of session or things will get inconsistent
type KickThreshold = ConstU32<PERIOD>;
type ValidatorId = <Self as frame_system::Config>::AccountId;
@@ -147,23 +147,31 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
.saturating_add(T::DbWeight::get().reads(3))
.saturating_add(T::DbWeight::get().writes(4))
}
/// Storage: CollatorSelection Invulnerables (r:1 w:1)
/// Proof: CollatorSelection Invulnerables (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen)
/// Storage: Session NextKeys (r:1 w:0)
/// Proof Skipped: Session NextKeys (max_values: None, max_size: None, mode: Measured)
/// The range of component `b` is `[1, 99]`.
fn add_invulnerable(b: u32, ) -> Weight {
/// Storage: CollatorSelection Invulnerables (r:1 w:1)
/// Proof: CollatorSelection Invulnerables (max_values: Some(1), max_size: Some(641), added: 1136, mode: MaxEncodedLen)
/// Storage: CollatorSelection Candidates (r:1 w:1)
/// Proof: CollatorSelection Candidates (max_values: Some(1), max_size: Some(4802), added: 5297, mode: MaxEncodedLen)
/// Storage: System Account (r:1 w:1)
/// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen)
/// The range of component `b` is `[1, 19]`.
/// The range of component `c` is `[1, 99]`.
fn add_invulnerable(b: u32, c: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `581 + b * (37 ±0)`
// Estimated: `4687 + b * (37 ±0)`
// Minimum execution time: 269_126_000 picoseconds.
Weight::from_parts(286_711_880, 0)
.saturating_add(Weight::from_parts(0, 4687))
// Standard Error: 22_887
.saturating_add(Weight::from_parts(813_399, 0).saturating_mul(b.into()))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(1))
// Measured: `757 + b * (32 ±0) + c * (53 ±0)`
// Estimated: `6287 + b * (37 ±0) + c * (53 ±0)`
// Minimum execution time: 52_720_000 picoseconds.
Weight::from_parts(56_102_459, 0)
.saturating_add(Weight::from_parts(0, 6287))
// Standard Error: 12_957
.saturating_add(Weight::from_parts(26_422, 0).saturating_mul(b.into()))
// Standard Error: 2_456
.saturating_add(Weight::from_parts(128_528, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(3))
.saturating_add(Weight::from_parts(0, 37).saturating_mul(b.into()))
.saturating_add(Weight::from_parts(0, 53).saturating_mul(c.into()))
}
/// Storage: CollatorSelection Invulnerables (r:1 w:1)
/// Proof: CollatorSelection Invulnerables (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen)
@@ -318,9 +318,9 @@ impl pallet_collator_selection::Config for Runtime {
type Currency = Balances;
type UpdateOrigin = CollatorSelectionUpdateOrigin;
type PotId = PotId;
type MaxCandidates = ConstU32<1000>;
type MinCandidates = ConstU32<0>;
type MaxInvulnerables = ConstU32<100>;
type MaxCandidates = ConstU32<100>;
type MinEligibleCollators = ConstU32<1>;
type MaxInvulnerables = ConstU32<20>;
// should be a multiple of session or things will get inconsistent
type KickThreshold = Period;
type ValidatorId = <Self as frame_system::Config>::AccountId;
@@ -511,10 +511,7 @@ impl pallet_aura::Config for Runtime {
parameter_types! {
pub const PotId: PalletId = PalletId(*b"PotStake");
pub const MaxCandidates: u32 = 1000;
pub const MinCandidates: u32 = 5;
pub const SessionLength: BlockNumber = 6 * HOURS;
pub const MaxInvulnerables: u32 = 100;
pub const ExecutiveBody: BodyId = BodyId::Executive;
}
@@ -526,9 +523,9 @@ impl pallet_collator_selection::Config for Runtime {
type Currency = Balances;
type UpdateOrigin = CollatorSelectionUpdateOrigin;
type PotId = PotId;
type MaxCandidates = MaxCandidates;
type MinCandidates = MinCandidates;
type MaxInvulnerables = MaxInvulnerables;
type MaxCandidates = ConstU32<100>;
type MinEligibleCollators = ConstU32<4>;
type MaxInvulnerables = ConstU32<20>;
// should be a multiple of session or things will get inconsistent
type KickThreshold = Period;
type ValidatorId = <Self as frame_system::Config>::AccountId;