Add simple collator election mechanism (#1340)

Fixes https://github.com/paritytech/polkadot-sdk/issues/106

Port of cumulus PR https://github.com/paritytech/cumulus/pull/2960

This PR adds the ability to bid for collator slots even after the max
number of collators have already registered. This eliminates the first
come, first served mechanism that was in place before.

Key changes:
- added `update_bond` extrinsic to allow registered candidates to adjust
their bonds in order to dynamically control their bids
- added `take_candidate_slot` extrinsic to try to replace an already
existing candidate by bidding more than them
- candidates are now kept in a sorted list in the pallet storage, where
the top `DesiredCandidates` out of `MaxCandidates` candidates in the
list will be selected by the session pallet as collators
- if the candidacy bond is increased through a `set_candidacy_bond`
call, candidates which don't meet the new bond requirements are kicked


# Checklist

- [ ] My PR includes a detailed description as outlined in the
"Description" section above
- [ ] My PR follows the [labeling
requirements](https://github.com/paritytech/polkadot-sdk/blob/master/docs/CONTRIBUTING.md#process)
of this project (at minimum one label for `T` required)
- [ ] I have made corresponding changes to the documentation (if
applicable)
- [ ] I have added tests that prove my fix is effective or that my
feature works (if applicable)
- [ ] If this PR alters any external APIs or interfaces used by
Polkadot, the corresponding Polkadot PR is ready as well as the
corresponding Cumulus PR (optional)

---------

Signed-off-by: georgepisaltu <george.pisaltu@parity.io>
This commit is contained in:
georgepisaltu
2023-11-14 18:22:30 +02:00
committed by GitHub
parent cd38ccff7f
commit 7d735fc8ae
13 changed files with 1731 additions and 163 deletions
@@ -25,14 +25,11 @@ use codec::Decode;
use frame_benchmarking::{
account, impl_benchmark_test_suite, v2::*, whitelisted_caller, BenchmarkError,
};
use frame_support::{
dispatch::DispatchResult,
traits::{Currency, EnsureOrigin, Get, ReservableCurrency},
};
use frame_support::traits::{Currency, EnsureOrigin, Get, ReservableCurrency};
use frame_system::{pallet_prelude::BlockNumberFor, EventRecord, RawOrigin};
use pallet_authorship::EventHandler;
use pallet_session::{self as session, SessionManager};
use sp_std::prelude::*;
use sp_std::{cmp, prelude::*};
pub type BalanceOf<T> =
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
@@ -94,7 +91,7 @@ fn register_candidates<T: Config>(count: u32) {
assert!(<CandidacyBond<T>>::get() > 0u32.into(), "Bond cannot be zero!");
for who in candidates {
T::Currency::make_free_balance_be(&who, <CandidacyBond<T>>::get() * 2u32.into());
T::Currency::make_free_balance_be(&who, <CandidacyBond<T>>::get() * 3u32.into());
<CollatorSelection<T>>::register_as_candidate(RawOrigin::Signed(who).into()).unwrap();
}
}
@@ -107,8 +104,11 @@ fn min_candidates<T: Config>() -> u32 {
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())
let candidates_length = <CandidateList<T>>::decode_len()
.unwrap_or_default()
.try_into()
.unwrap_or_default();
min_collators.saturating_sub(candidates_length)
}
#[benchmarks(where T: pallet_authorship::Config + session::Config)]
@@ -160,22 +160,19 @@ mod benchmarks {
.unwrap();
}
// ... and register them.
for (who, _) in candidates {
for (who, _) in candidates.iter() {
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(())
T::Currency::make_free_balance_be(who, deposit * 1000_u32.into());
<CandidateList<T>>::try_mutate(|list| {
list.try_push(CandidateInfo { who: who.clone(), deposit }).unwrap();
Ok::<(), BenchmarkError>(())
})
.expect("only returns ok");
.unwrap();
T::Currency::reserve(who, deposit)?;
<LastAuthoredBlock<T>>::insert(
who.clone(),
frame_system::Pallet::<T>::block_number() + T::KickThreshold::get(),
);
}
// now we need to fill up invulnerables
@@ -226,10 +223,27 @@ mod benchmarks {
}
#[benchmark]
fn set_candidacy_bond() -> Result<(), BenchmarkError> {
let bond_amount: BalanceOf<T> = T::Currency::minimum_balance() * 10u32.into();
fn set_candidacy_bond(
c: Linear<0, { T::MaxCandidates::get() }>,
k: Linear<0, { T::MaxCandidates::get() }>,
) -> Result<(), BenchmarkError> {
let initial_bond_amount: BalanceOf<T> = T::Currency::minimum_balance() * 2u32.into();
<CandidacyBond<T>>::put(initial_bond_amount);
register_validators::<T>(c);
register_candidates::<T>(c);
let kicked = cmp::min(k, c);
let origin =
T::UpdateOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
let bond_amount = if k > 0 {
<CandidateList<T>>::mutate(|candidates| {
for info in candidates.iter_mut().skip(kicked as usize) {
info.deposit = T::Currency::minimum_balance() * 3u32.into();
}
});
T::Currency::minimum_balance() * 3u32.into()
} else {
T::Currency::minimum_balance()
};
#[extrinsic_call]
_(origin as T::RuntimeOrigin, bond_amount);
@@ -238,6 +252,35 @@ mod benchmarks {
Ok(())
}
#[benchmark]
fn update_bond(
c: Linear<{ min_candidates::<T>() + 1 }, { T::MaxCandidates::get() }>,
) -> Result<(), BenchmarkError> {
<CandidacyBond<T>>::put(T::Currency::minimum_balance());
<DesiredCandidates<T>>::put(c);
register_validators::<T>(c);
register_candidates::<T>(c);
let caller = <CandidateList<T>>::get()[0].who.clone();
v2::whitelist!(caller);
let bond_amount: BalanceOf<T> =
T::Currency::minimum_balance() + T::Currency::minimum_balance();
#[extrinsic_call]
_(RawOrigin::Signed(caller.clone()), bond_amount);
assert_last_event::<T>(
Event::CandidateBondUpdated { account_id: caller, deposit: bond_amount }.into(),
);
assert!(
<CandidateList<T>>::get().iter().last().unwrap().deposit ==
T::Currency::minimum_balance() * 2u32.into()
);
Ok(())
}
// worse case is when we have all the max-candidate slots filled except one, and we fill that
// one.
#[benchmark]
@@ -267,6 +310,36 @@ mod benchmarks {
);
}
#[benchmark]
fn take_candidate_slot(c: Linear<{ min_candidates::<T>() + 1 }, { T::MaxCandidates::get() }>) {
<CandidacyBond<T>>::put(T::Currency::minimum_balance());
<DesiredCandidates<T>>::put(1);
register_validators::<T>(c);
register_candidates::<T>(c);
let caller: T::AccountId = whitelisted_caller();
let bond: BalanceOf<T> = T::Currency::minimum_balance() * 10u32.into();
T::Currency::make_free_balance_be(&caller, bond);
<session::Pallet<T>>::set_keys(
RawOrigin::Signed(caller.clone()).into(),
keys::<T>(c + 1),
Vec::new(),
)
.unwrap();
let target = <CandidateList<T>>::get().iter().last().unwrap().who.clone();
#[extrinsic_call]
_(RawOrigin::Signed(caller.clone()), bond / 2u32.into(), target.clone());
assert_last_event::<T>(
Event::CandidateReplaced { old: target, new: caller, deposit: bond / 2u32.into() }
.into(),
);
}
// worse case is the last candidate leaving.
#[benchmark]
fn leave_intent(c: Linear<{ min_candidates::<T>() + 1 }, { T::MaxCandidates::get() }>) {
@@ -276,7 +349,7 @@ mod benchmarks {
register_validators::<T>(c);
register_candidates::<T>(c);
let leaving = <Candidates<T>>::get().last().unwrap().who.clone();
let leaving = <CandidateList<T>>::get().iter().last().unwrap().who.clone();
v2::whitelist!(leaving);
#[extrinsic_call]
@@ -323,31 +396,37 @@ mod benchmarks {
let new_block: BlockNumberFor<T> = 1800u32.into();
let zero_block: BlockNumberFor<T> = 0u32.into();
let candidates = <Candidates<T>>::get();
let candidates: Vec<T::AccountId> = <CandidateList<T>>::get()
.iter()
.map(|candidate_info| candidate_info.who.clone())
.collect();
let non_removals = c.saturating_sub(r);
for i in 0..c {
<LastAuthoredBlock<T>>::insert(candidates[i as usize].who.clone(), zero_block);
<LastAuthoredBlock<T>>::insert(candidates[i as usize].clone(), zero_block);
}
if non_removals > 0 {
for i in 0..non_removals {
<LastAuthoredBlock<T>>::insert(candidates[i as usize].who.clone(), new_block);
<LastAuthoredBlock<T>>::insert(candidates[i as usize].clone(), new_block);
}
} else {
for i in 0..c {
<LastAuthoredBlock<T>>::insert(candidates[i as usize].who.clone(), new_block);
<LastAuthoredBlock<T>>::insert(candidates[i as usize].clone(), new_block);
}
}
let min_candidates = min_candidates::<T>();
let pre_length = <Candidates<T>>::get().len();
let pre_length = <CandidateList<T>>::decode_len().unwrap_or_default();
frame_system::Pallet::<T>::set_block_number(new_block);
assert!(<Candidates<T>>::get().len() == c as usize);
let current_length: u32 = <CandidateList<T>>::decode_len()
.unwrap_or_default()
.try_into()
.unwrap_or_default();
assert!(c == current_length);
#[block]
{
<CollatorSelection<T> as SessionManager<_>>::new_session(0);
@@ -357,16 +436,20 @@ mod benchmarks {
// 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);
assert!(<CandidateList<T>>::decode_len().unwrap_or_default() < pre_length);
} 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);
let current_length: u32 = <CandidateList<T>>::decode_len()
.unwrap_or_default()
.try_into()
.unwrap_or_default();
assert!(min_candidates == current_length);
} else {
// removals >= candidates, non removals must == 0
// can't remove more than exist
assert!(<Candidates<T>>::get().len() == pre_length);
assert!(<CandidateList<T>>::decode_len().unwrap_or_default() == pre_length);
}
}
+361 -76
View File
@@ -35,16 +35,36 @@
//!
//! 1. [`Invulnerables`]: a set of collators appointed by governance. These accounts will always be
//! collators.
//! 2. [`Candidates`]: these are *candidates to the collation task* and may or may not be elected as
//! a final collator.
//! 2. [`CandidateList`]: these are *candidates to the collation task* and may or may not be elected
//! as a final collator.
//!
//! The current implementation resolves congestion of [`Candidates`] in a first-come-first-serve
//! manner.
//! The current implementation resolves congestion of [`CandidateList`] through a simple auction
//! mechanism. Candidates bid for the collator slots and at the end of the session, the auction ends
//! and the top candidates are selected to become collators. The number of selected candidates is
//! determined by the value of `DesiredCandidates`.
//!
//! Before the list reaches full capacity, candidates can register by placing the minimum bond
//! through `register_as_candidate`. Then, if an account wants to participate in the collator slot
//! auction, they have to replace an existing candidate by placing a greater deposit through
//! `take_candidate_slot`. Existing candidates can increase their bids through `update_bond`.
//!
//! At any point, an account can take the place of another account in the candidate list if they put
//! up a greater deposit than the target. While new joiners would like to deposit as little as
//! possible to participate in the auction, the replacement threat incentivizes candidates to bid as
//! close to their budget as possible in order to avoid being replaced.
//!
//! Candidates which are not on "winning" slots in the list can also decrease their deposits through
//! `update_bond`, but candidates who are on top slots and try to decrease their deposits will fail
//! in order to enforce auction mechanics and have meaningful bids.
//!
//! 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.
//!
//! When a new session starts, candidates with the highest deposits will be selected in order until
//! the desired number of collators is reached. Candidates can increase or decrease their deposits
//! between sessions in order to ensure they receive a slot in the collator list.
//!
//! ### Rewards
//!
//! The Collator Selection pallet maintains an on-chain account (the "Pot"). In each block, the
@@ -56,8 +76,8 @@
//!
//! 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).
//! Note: Eventually the Pot distribution may be modified as discussed in [this
//! issue](https://github.com/paritytech/statemint/issues/21#issuecomment-810481073).
#![cfg_attr(not(feature = "std"), no_std)]
@@ -182,9 +202,12 @@ pub mod pallet {
/// The (community, limited) collation candidates. `Candidates` and `Invulnerables` should be
/// mutually exclusive.
///
/// This list is sorted in ascending order by deposit and when the deposits are equal, the least
/// recently updated is considered greater.
#[pallet::storage]
#[pallet::getter(fn candidates)]
pub type Candidates<T: Config> = StorageValue<
#[pallet::getter(fn candidate_list)]
pub type CandidateList<T: Config> = StorageValue<
_,
BoundedVec<CandidateInfo<T::AccountId, BalanceOf<T>>, T::MaxCandidates>,
ValueQuery,
@@ -261,8 +284,12 @@ pub mod pallet {
NewCandidacyBond { bond_amount: BalanceOf<T> },
/// A new candidate joined.
CandidateAdded { account_id: T::AccountId, deposit: BalanceOf<T> },
/// Bond of a candidate updated.
CandidateBondUpdated { account_id: T::AccountId, deposit: BalanceOf<T> },
/// A candidate was removed.
CandidateRemoved { account_id: T::AccountId },
/// An account was replaced in the candidate list by another one.
CandidateReplaced { old: T::AccountId, new: T::AccountId, deposit: BalanceOf<T> },
/// 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 },
@@ -288,12 +315,38 @@ pub mod pallet {
NoAssociatedValidatorId,
/// Validator ID is not yet registered.
ValidatorNotRegistered,
/// Could not insert in the candidate list.
InsertToCandidateListFailed,
/// Could not remove from the candidate list.
RemoveFromCandidateListFailed,
/// New deposit amount would be below the minimum candidacy bond.
DepositTooLow,
/// Could not update the candidate list.
UpdateCandidateListFailed,
/// Deposit amount is too low to take the target's slot in the candidate list.
InsufficientBond,
/// The target account to be replaced in the candidate list is not a candidate.
TargetIsNotCandidate,
/// The updated deposit amount is equal to the amount already reserved.
IdenticalDeposit,
/// Cannot lower candidacy bond while occupying a future collator slot in the list.
InvalidUnreserve,
}
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn integrity_test() {
assert!(T::MinEligibleCollators::get() > 0, "chain must require at least one collator");
assert!(
T::MaxInvulnerables::get().saturating_add(T::MaxCandidates::get()) >=
T::MinEligibleCollators::get(),
"invulnerables and candidates must be able to satisfy collator demand"
);
}
#[cfg(feature = "try-runtime")]
fn try_state(_: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
Self::do_try_state()
}
}
@@ -307,8 +360,8 @@ pub mod pallet {
/// 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
/// 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`.
@@ -319,8 +372,9 @@ pub mod pallet {
// don't wipe out the collator set
if new.is_empty() {
// Casting `u32` to `usize` should be safe on all machines running this.
ensure!(
Candidates::<T>::decode_len().unwrap_or_default() >=
CandidateList::<T>::decode_len().unwrap_or_default() >=
T::MinEligibleCollators::get() as usize,
Error::<T>::TooFewEligibleCollators
);
@@ -401,17 +455,47 @@ pub mod pallet {
/// Set the candidacy bond amount.
///
/// If the candidacy bond is increased by this call, all current candidates which have a
/// deposit lower than the new bond will be kicked from the list and get their deposits
/// back.
///
/// The origin for this call must be the `UpdateOrigin`.
#[pallet::call_index(2)]
#[pallet::weight(T::WeightInfo::set_candidacy_bond())]
#[pallet::weight(T::WeightInfo::set_candidacy_bond(
T::MaxCandidates::get(),
T::MaxCandidates::get()
))]
pub fn set_candidacy_bond(
origin: OriginFor<T>,
bond: BalanceOf<T>,
) -> DispatchResultWithPostInfo {
T::UpdateOrigin::ensure_origin(origin)?;
<CandidacyBond<T>>::put(bond);
let bond_increased = <CandidacyBond<T>>::mutate(|old_bond| -> bool {
let bond_increased = *old_bond < bond;
*old_bond = bond;
bond_increased
});
let initial_len = <CandidateList<T>>::decode_len().unwrap_or_default();
let kicked = (bond_increased && initial_len > 0)
.then(|| {
// Closure below returns the number of candidates which were kicked because
// their deposits were lower than the new candidacy bond.
<CandidateList<T>>::mutate(|candidates| -> usize {
let first_safe_candidate = candidates
.iter()
.position(|candidate| candidate.deposit >= bond)
.unwrap_or(initial_len);
let kicked_candidates = candidates.drain(..first_safe_candidate);
for candidate in kicked_candidates {
T::Currency::unreserve(&candidate.who, candidate.deposit);
<LastAuthoredBlock<T>>::remove(candidate.who);
}
first_safe_candidate
})
})
.unwrap_or_default();
Self::deposit_event(Event::NewCandidacyBond { bond_amount: bond });
Ok(().into())
Ok(Some(T::WeightInfo::set_candidacy_bond(initial_len as u32, kicked as u32)).into())
}
/// Register this account as a collator candidate. The account must (a) already have
@@ -424,8 +508,11 @@ pub mod pallet {
let who = ensure_signed(origin)?;
// ensure we are below limit.
let length = <Candidates<T>>::decode_len().unwrap_or_default();
ensure!((length as u32) < Self::desired_candidates(), Error::<T>::TooManyCandidates);
let length: u32 = <CandidateList<T>>::decode_len()
.unwrap_or_default()
.try_into()
.unwrap_or_default();
ensure!(length < T::MaxCandidates::get(), Error::<T>::TooManyCandidates);
ensure!(!Self::invulnerables().contains(&who), Error::<T>::AlreadyInvulnerable);
let validator_key = T::ValidatorIdOf::convert(who.clone())
@@ -437,25 +524,27 @@ pub mod pallet {
let deposit = Self::candidacy_bond();
// First authored block is current block plus kick threshold to handle session delay
let incoming = CandidateInfo { who: who.clone(), deposit };
let current_count =
<Candidates<T>>::try_mutate(|candidates| -> Result<usize, DispatchError> {
if candidates.iter().any(|candidate| candidate.who == who) {
Err(Error::<T>::AlreadyCandidate)?
} else {
T::Currency::reserve(&who, deposit)?;
candidates.try_push(incoming).map_err(|_| Error::<T>::TooManyCandidates)?;
<LastAuthoredBlock<T>>::insert(
who.clone(),
frame_system::Pallet::<T>::block_number() + T::KickThreshold::get(),
);
Ok(candidates.len())
}
})?;
<CandidateList<T>>::try_mutate(|candidates| -> Result<(), DispatchError> {
ensure!(
!candidates.iter().any(|candidate_info| candidate_info.who == who),
Error::<T>::AlreadyCandidate
);
T::Currency::reserve(&who, deposit)?;
<LastAuthoredBlock<T>>::insert(
who.clone(),
frame_system::Pallet::<T>::block_number() + T::KickThreshold::get(),
);
candidates
.try_insert(0, CandidateInfo { who: who.clone(), deposit })
.map_err(|_| Error::<T>::InsertToCandidateListFailed)?;
Ok(())
})?;
Self::deposit_event(Event::CandidateAdded { account_id: who, deposit });
Ok(Some(T::WeightInfo::register_as_candidate(current_count as u32)).into())
// Safe to do unchecked add here because we ensure above that `length <
// T::MaxCandidates::get()`, and since `T::MaxCandidates` is `u32` it can be at most
// `u32::MAX`, therefore `length + 1` cannot overflow.
Ok(Some(T::WeightInfo::register_as_candidate(length + 1)).into())
}
/// Deregister `origin` as a collator candidate. Note that the collator can only leave on
@@ -468,13 +557,14 @@ pub mod pallet {
pub fn leave_intent(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
let who = ensure_signed(origin)?;
ensure!(
Self::eligible_collators() > T::MinEligibleCollators::get() as usize,
Self::eligible_collators() > T::MinEligibleCollators::get(),
Error::<T>::TooFewEligibleCollators
);
let length = <CandidateList<T>>::decode_len().unwrap_or_default();
// Do remove their last authored block.
let current_count = Self::try_remove_candidate(&who, true)?;
Self::try_remove_candidate(&who, true)?;
Ok(Some(T::WeightInfo::leave_intent(current_count as u32)).into())
Ok(Some(T::WeightInfo::leave_intent(length.saturating_sub(1) as u32)).into())
}
/// Add a new account `who` to the list of `Invulnerables` collators. `who` must have
@@ -521,7 +611,7 @@ pub mod pallet {
.unwrap_or_default()
.try_into()
.unwrap_or(T::MaxInvulnerables::get().saturating_sub(1)),
Candidates::<T>::decode_len()
<CandidateList<T>>::decode_len()
.unwrap_or_default()
.try_into()
.unwrap_or(T::MaxCandidates::get()),
@@ -540,7 +630,7 @@ pub mod pallet {
T::UpdateOrigin::ensure_origin(origin)?;
ensure!(
Self::eligible_collators() > T::MinEligibleCollators::get() as usize,
Self::eligible_collators() > T::MinEligibleCollators::get(),
Error::<T>::TooFewEligibleCollators
);
@@ -554,6 +644,154 @@ pub mod pallet {
Self::deposit_event(Event::InvulnerableRemoved { account_id: who });
Ok(())
}
/// Update the candidacy bond of collator candidate `origin` to a new amount `new_deposit`.
///
/// Setting a `new_deposit` that is lower than the current deposit while `origin` is
/// occupying a top-`DesiredCandidates` slot is not allowed.
///
/// This call will fail if `origin` is not a collator candidate, the updated bond is lower
/// than the minimum candidacy bond, and/or the amount cannot be reserved.
#[pallet::call_index(7)]
#[pallet::weight(T::WeightInfo::update_bond(T::MaxCandidates::get()))]
pub fn update_bond(
origin: OriginFor<T>,
new_deposit: BalanceOf<T>,
) -> DispatchResultWithPostInfo {
let who = ensure_signed(origin)?;
ensure!(new_deposit >= <CandidacyBond<T>>::get(), Error::<T>::DepositTooLow);
// The function below will try to mutate the `CandidateList` entry for the caller to
// update their deposit to the new value of `new_deposit`. The return value is the
// position of the entry in the list, used for weight calculation.
let length =
<CandidateList<T>>::try_mutate(|candidates| -> Result<usize, DispatchError> {
let idx = candidates
.iter()
.position(|candidate_info| candidate_info.who == who)
.ok_or_else(|| Error::<T>::NotCandidate)?;
let candidate_count = candidates.len();
// Remove the candidate from the list.
let mut info = candidates.remove(idx);
let old_deposit = info.deposit;
if new_deposit > old_deposit {
T::Currency::reserve(&who, new_deposit - old_deposit)?;
} else if new_deposit < old_deposit {
// Casting `u32` to `usize` should be safe on all machines running this.
ensure!(
idx.saturating_add(<DesiredCandidates<T>>::get() as usize) <
candidate_count,
Error::<T>::InvalidUnreserve
);
T::Currency::unreserve(&who, old_deposit - new_deposit);
} else {
return Err(Error::<T>::IdenticalDeposit.into())
}
// Update the deposit and insert the candidate in the correct spot in the list.
info.deposit = new_deposit;
let new_pos = candidates
.iter()
.position(|candidate| candidate.deposit >= new_deposit)
.unwrap_or_else(|| candidates.len());
candidates
.try_insert(new_pos, info)
.map_err(|_| Error::<T>::InsertToCandidateListFailed)?;
Ok(candidate_count)
})?;
Self::deposit_event(Event::CandidateBondUpdated {
account_id: who,
deposit: new_deposit,
});
Ok(Some(T::WeightInfo::update_bond(length as u32)).into())
}
/// The caller `origin` replaces a candidate `target` in the collator candidate list by
/// reserving `deposit`. The amount `deposit` reserved by the caller must be greater than
/// the existing bond of the target it is trying to replace.
///
/// This call will fail if the caller is already a collator candidate or invulnerable, the
/// caller does not have registered session keys, the target is not a collator candidate,
/// and/or the `deposit` amount cannot be reserved.
#[pallet::call_index(8)]
#[pallet::weight(T::WeightInfo::take_candidate_slot(T::MaxCandidates::get()))]
pub fn take_candidate_slot(
origin: OriginFor<T>,
deposit: BalanceOf<T>,
target: T::AccountId,
) -> DispatchResultWithPostInfo {
let who = ensure_signed(origin)?;
ensure!(!Self::invulnerables().contains(&who), Error::<T>::AlreadyInvulnerable);
ensure!(deposit >= Self::candidacy_bond(), Error::<T>::InsufficientBond);
let validator_key = T::ValidatorIdOf::convert(who.clone())
.ok_or(Error::<T>::NoAssociatedValidatorId)?;
ensure!(
T::ValidatorRegistration::is_registered(&validator_key),
Error::<T>::ValidatorNotRegistered
);
let length = <CandidateList<T>>::decode_len().unwrap_or_default();
// The closure below iterates through all elements of the candidate list to ensure that
// the caller isn't already a candidate and to find the target it's trying to replace in
// the list. The return value is a tuple of the position of the candidate to be replaced
// in the list along with its candidate information.
let target_info = <CandidateList<T>>::try_mutate(
|candidates| -> Result<CandidateInfo<T::AccountId, BalanceOf<T>>, DispatchError> {
// Find the position in the list of the candidate that is being replaced.
let mut target_info_idx = None;
let mut new_info_idx = None;
for (idx, candidate_info) in candidates.iter().enumerate() {
// While iterating through the candidates trying to find the target,
// also ensure on the same pass that our caller isn't already a
// candidate.
ensure!(candidate_info.who != who, Error::<T>::AlreadyCandidate);
// If we find our target, update the position but do not stop the
// iteration since we're also checking that the caller isn't already a
// candidate.
if candidate_info.who == target {
target_info_idx = Some(idx);
}
// Find the spot where the new candidate would be inserted in the current
// version of the list.
if new_info_idx.is_none() && candidate_info.deposit >= deposit {
new_info_idx = Some(idx);
}
}
let target_info_idx =
target_info_idx.ok_or(Error::<T>::TargetIsNotCandidate)?;
// Remove the old candidate from the list.
let target_info = candidates.remove(target_info_idx);
ensure!(deposit > target_info.deposit, Error::<T>::InsufficientBond);
// We have removed one element before `new_info_idx`, so the position we have to
// insert to is reduced by 1.
let new_pos = new_info_idx
.map(|i| i.saturating_sub(1))
.unwrap_or_else(|| candidates.len());
let new_info = CandidateInfo { who: who.clone(), deposit };
// Insert the new candidate in the correct spot in the list.
candidates
.try_insert(new_pos, new_info)
.expect("candidate count previously decremented; qed");
Ok(target_info)
},
)?;
T::Currency::reserve(&who, deposit)?;
T::Currency::unreserve(&target_info.who, target_info.deposit);
<LastAuthoredBlock<T>>::remove(target_info.who.clone());
<LastAuthoredBlock<T>>::insert(
who.clone(),
frame_system::Pallet::<T>::block_number() + T::KickThreshold::get(),
);
Self::deposit_event(Event::CandidateReplaced { old: target, new: who, deposit });
Ok(Some(T::WeightInfo::take_candidate_slot(length as u32)).into())
}
}
impl<T: Config> Pallet<T> {
@@ -564,84 +802,122 @@ pub mod pallet {
/// Return the total number of accounts that are eligible collators (candidates and
/// invulnerables).
fn eligible_collators() -> usize {
Candidates::<T>::decode_len()
fn eligible_collators() -> u32 {
<CandidateList<T>>::decode_len()
.unwrap_or_default()
.saturating_add(Invulnerables::<T>::decode_len().unwrap_or_default())
.try_into()
.unwrap_or(u32::MAX)
}
/// 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
.iter()
.position(|candidate| candidate.who == *who)
.ok_or(Error::<T>::NotCandidate)?;
let candidate = candidates.remove(index);
T::Currency::unreserve(who, candidate.deposit);
if remove_last_authored {
<LastAuthoredBlock<T>>::remove(who.clone())
};
Ok(candidates.len())
})?;
) -> Result<(), DispatchError> {
<CandidateList<T>>::try_mutate(|candidates| -> Result<(), DispatchError> {
let idx = candidates
.iter()
.position(|candidate_info| candidate_info.who == *who)
.ok_or(Error::<T>::NotCandidate)?;
let deposit = candidates[idx].deposit;
T::Currency::unreserve(who, deposit);
candidates.remove(idx);
if remove_last_authored {
<LastAuthoredBlock<T>>::remove(who.clone())
};
Ok(())
})?;
Self::deposit_event(Event::CandidateRemoved { account_id: who.clone() });
Ok(current_count)
Ok(())
}
/// Assemble the current set of candidates and invulnerables into the next collator set.
///
/// This is done on the fly, as frequent as we are told to do so, as the session manager.
pub fn assemble_collators(
candidates: BoundedVec<T::AccountId, T::MaxCandidates>,
) -> Vec<T::AccountId> {
pub fn assemble_collators() -> Vec<T::AccountId> {
// Casting `u32` to `usize` should be safe on all machines running this.
let desired_candidates = <DesiredCandidates<T>>::get() as usize;
let mut collators = Self::invulnerables().to_vec();
collators.extend(candidates);
collators.extend(
<CandidateList<T>>::get()
.iter()
.rev()
.cloned()
.take(desired_candidates)
.map(|candidate_info| candidate_info.who),
);
collators
}
/// 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> {
///
/// Return value is the number of candidates left in the list.
pub fn kick_stale_candidates(candidates: impl IntoIterator<Item = T::AccountId>) -> u32 {
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 last_block = <LastAuthoredBlock<T>>::get(c.clone());
let since_last = now.saturating_sub(last_block);
let is_invulnerable = Self::invulnerables().contains(&c.who);
let is_invulnerable = Self::invulnerables().contains(&c);
let is_lazy = since_last >= kick_threshold;
if is_invulnerable {
// They are invulnerable. No reason for them to be in Candidates also.
// They are invulnerable. No reason for them to be in `CandidateList` 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);
let _ = Self::try_remove_candidate(&c, false);
None
} else {
if Self::eligible_collators() <= min_collators as usize || !is_lazy {
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)
Some(c)
} else {
// This collator has not produced a block recently enough. Bye bye.
let _ = Self::try_remove_candidate(&c.who, true);
let _ = Self::try_remove_candidate(&c, true);
None
}
}
})
.collect::<Vec<_>>()
.count()
.try_into()
.expect("filter_map operation can't result in a bounded vec larger than its original; qed")
}
/// Ensure the correctness of the state of this pallet.
///
/// This should be valid before or after each state transition of this pallet.
///
/// # Invariants
///
/// ## `DesiredCandidates`
///
/// * The current desired candidate count should not exceed the candidate list capacity.
/// * The number of selected candidates together with the invulnerables must be greater than
/// or equal to the minimum number of eligible collators.
#[cfg(any(test, feature = "try-runtime"))]
pub fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> {
let desired_candidates = <DesiredCandidates<T>>::get();
frame_support::ensure!(
desired_candidates <= T::MaxCandidates::get(),
"Shouldn't demand more candidates than the pallet config allows."
);
frame_support::ensure!(
desired_candidates.saturating_add(T::MaxInvulnerables::get()) >=
T::MinEligibleCollators::get(),
"Invulnerable set together with desired candidates should be able to meet the collator quota."
);
Ok(())
}
}
/// Keep track of number of authored blocks per authority, uncles are counted as well since
@@ -677,14 +953,23 @@ pub mod pallet {
<frame_system::Pallet<T>>::block_number(),
);
let candidates = Self::candidates();
let candidates_len_before = candidates.len();
let active_candidates = Self::kick_stale_candidates(candidates);
let removed = candidates_len_before - active_candidates.len();
let result = Self::assemble_collators(active_candidates);
// The `expect` below is safe because the list is a `BoundedVec` with a max size of
// `T::MaxCandidates`, which is a `u32`. When `decode_len` returns `Some(len)`, `len`
// must be valid and at most `u32::MAX`, which must always be able to convert to `u32`.
let candidates_len_before: u32 = <CandidateList<T>>::decode_len()
.unwrap_or_default()
.try_into()
.expect("length is at most `T::MaxCandidates`, so it must fit in `u32`; qed");
let active_candidates_count = Self::kick_stale_candidates(
<CandidateList<T>>::get()
.iter()
.map(|candidate_info| candidate_info.who.clone()),
);
let removed = candidates_len_before.saturating_sub(active_candidates_count);
let result = Self::assemble_collators();
frame_system::Pallet::<T>::register_extra_weight_unchecked(
T::WeightInfo::new_session(candidates_len_before as u32, removed as u32),
T::WeightInfo::new_session(candidates_len_before, removed),
DispatchClass::Mandatory,
);
Some(result)
File diff suppressed because it is too large Load Diff
@@ -30,9 +30,11 @@ pub trait WeightInfo {
fn add_invulnerable(_b: u32, _c: u32) -> Weight;
fn remove_invulnerable(_b: u32) -> Weight;
fn set_desired_candidates() -> Weight;
fn set_candidacy_bond() -> Weight;
fn set_candidacy_bond(_c: u32, _k: u32) -> Weight;
fn register_as_candidate(_c: u32) -> Weight;
fn leave_intent(_c: u32) -> Weight;
fn update_bond(_c: u32) -> Weight;
fn take_candidate_slot(_c: u32) -> Weight;
fn note_author() -> Weight;
fn new_session(_c: u32, _r: u32) -> Weight;
}
@@ -49,7 +51,7 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
fn set_desired_candidates() -> Weight {
Weight::from_parts(16_363_000_u64, 0).saturating_add(T::DbWeight::get().writes(1_u64))
}
fn set_candidacy_bond() -> Weight {
fn set_candidacy_bond(_c: u32, _k: u32) -> Weight {
Weight::from_parts(16_840_000_u64, 0).saturating_add(T::DbWeight::get().writes(1_u64))
}
fn register_as_candidate(c: u32) -> Weight {
@@ -66,6 +68,20 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
.saturating_add(T::DbWeight::get().reads(1_u64))
.saturating_add(T::DbWeight::get().writes(2_u64))
}
fn update_bond(c: u32) -> Weight {
Weight::from_parts(55_336_000_u64, 0)
// Standard Error: 0
.saturating_add(Weight::from_parts(151_000_u64, 0).saturating_mul(c as u64))
.saturating_add(T::DbWeight::get().reads(1_u64))
.saturating_add(T::DbWeight::get().writes(2_u64))
}
fn take_candidate_slot(c: u32) -> Weight {
Weight::from_parts(71_196_000_u64, 0)
// Standard Error: 0
.saturating_add(Weight::from_parts(198_000_u64, 0).saturating_mul(c as u64))
.saturating_add(T::DbWeight::get().reads(4_u64))
.saturating_add(T::DbWeight::get().writes(2_u64))
}
fn note_author() -> Weight {
Weight::from_parts(71_461_000_u64, 0)
.saturating_add(T::DbWeight::get().reads(3_u64))
@@ -136,7 +152,7 @@ impl WeightInfo for () {
fn set_desired_candidates() -> Weight {
Weight::from_parts(16_363_000_u64, 0).saturating_add(RocksDbWeight::get().writes(1_u64))
}
fn set_candidacy_bond() -> Weight {
fn set_candidacy_bond(_c: u32, _k: u32) -> Weight {
Weight::from_parts(16_840_000_u64, 0).saturating_add(RocksDbWeight::get().writes(1_u64))
}
fn register_as_candidate(c: u32) -> Weight {
@@ -158,6 +174,20 @@ impl WeightInfo for () {
.saturating_add(RocksDbWeight::get().reads(3_u64))
.saturating_add(RocksDbWeight::get().writes(4_u64))
}
fn update_bond(c: u32) -> Weight {
Weight::from_parts(55_336_000_u64, 0)
// Standard Error: 0
.saturating_add(Weight::from_parts(151_000_u64, 0).saturating_mul(c as u64))
.saturating_add(RocksDbWeight::get().reads(3_u64))
.saturating_add(RocksDbWeight::get().writes(4_u64))
}
fn take_candidate_slot(c: u32) -> Weight {
Weight::from_parts(71_196_000_u64, 0)
// Standard Error: 0
.saturating_add(Weight::from_parts(198_000_u64, 0).saturating_mul(c as u64))
.saturating_add(RocksDbWeight::get().reads(3_u64))
.saturating_add(RocksDbWeight::get().writes(4_u64))
}
fn new_session(r: u32, c: u32) -> Weight {
Weight::from_parts(0_u64, 0)
// Standard Error: 1_010_000
@@ -123,7 +123,7 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
}
/// Storage: `CollatorSelection::CandidacyBond` (r:0 w:1)
/// Proof: `CollatorSelection::CandidacyBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`)
fn set_candidacy_bond() -> Weight {
fn set_candidacy_bond(_c: u32, _k: u32) -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
@@ -177,6 +177,30 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
fn update_bond(c: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `306 + c * (50 ±0)`
// Estimated: `6287`
// Minimum execution time: 34_814_000 picoseconds.
Weight::from_parts(36_371_520, 0)
.saturating_add(Weight::from_parts(0, 6287))
// Standard Error: 2_391
.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
fn take_candidate_slot(c: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `306 + c * (50 ±0)`
// Estimated: `6287`
// Minimum execution time: 34_814_000 picoseconds.
Weight::from_parts(36_371_520, 0)
.saturating_add(Weight::from_parts(0, 6287))
// Standard Error: 2_391
.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
/// Storage: `System::Account` (r:2 w:2)
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
/// Storage: `System::BlockWeight` (r:1 w:1)
@@ -121,7 +121,7 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
}
/// Storage: `CollatorSelection::CandidacyBond` (r:0 w:1)
/// Proof: `CollatorSelection::CandidacyBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`)
fn set_candidacy_bond() -> Weight {
fn set_candidacy_bond(_c: u32, _k: u32) -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
@@ -175,6 +175,30 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
fn update_bond(c: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `306 + c * (50 ±0)`
// Estimated: `6287`
// Minimum execution time: 34_814_000 picoseconds.
Weight::from_parts(36_371_520, 0)
.saturating_add(Weight::from_parts(0, 6287))
// Standard Error: 2_391
.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
fn take_candidate_slot(c: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `306 + c * (50 ±0)`
// Estimated: `6287`
// Minimum execution time: 34_814_000 picoseconds.
Weight::from_parts(36_371_520, 0)
.saturating_add(Weight::from_parts(0, 6287))
// Standard Error: 2_391
.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
/// Storage: `System::Account` (r:2 w:2)
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
/// Storage: `System::BlockWeight` (r:1 w:1)
@@ -124,7 +124,7 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
}
/// Storage: `CollatorSelection::CandidacyBond` (r:0 w:1)
/// Proof: `CollatorSelection::CandidacyBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`)
fn set_candidacy_bond() -> Weight {
fn set_candidacy_bond(_c: u32, _k: u32) -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
@@ -178,6 +178,30 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
fn update_bond(c: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `306 + c * (50 ±0)`
// Estimated: `6287`
// Minimum execution time: 34_814_000 picoseconds.
Weight::from_parts(36_371_520, 0)
.saturating_add(Weight::from_parts(0, 6287))
// Standard Error: 2_391
.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
fn take_candidate_slot(c: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `306 + c * (50 ±0)`
// Estimated: `6287`
// Minimum execution time: 34_814_000 picoseconds.
Weight::from_parts(36_371_520, 0)
.saturating_add(Weight::from_parts(0, 6287))
// Standard Error: 2_391
.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
/// Storage: `System::Account` (r:2 w:2)
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
/// Storage: `System::BlockWeight` (r:1 w:1)
@@ -123,7 +123,7 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
}
/// Storage: `CollatorSelection::CandidacyBond` (r:0 w:1)
/// Proof: `CollatorSelection::CandidacyBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`)
fn set_candidacy_bond() -> Weight {
fn set_candidacy_bond(_c: u32, _k: u32) -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
@@ -177,6 +177,30 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
fn update_bond(c: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `306 + c * (50 ±0)`
// Estimated: `6287`
// Minimum execution time: 34_814_000 picoseconds.
Weight::from_parts(36_371_520, 0)
.saturating_add(Weight::from_parts(0, 6287))
// Standard Error: 2_391
.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
fn take_candidate_slot(c: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `306 + c * (50 ±0)`
// Estimated: `6287`
// Minimum execution time: 34_814_000 picoseconds.
Weight::from_parts(36_371_520, 0)
.saturating_add(Weight::from_parts(0, 6287))
// Standard Error: 2_391
.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
/// Storage: `System::Account` (r:2 w:2)
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
/// Storage: `System::BlockWeight` (r:1 w:1)
@@ -123,7 +123,7 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
}
/// Storage: `CollatorSelection::CandidacyBond` (r:0 w:1)
/// Proof: `CollatorSelection::CandidacyBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`)
fn set_candidacy_bond() -> Weight {
fn set_candidacy_bond(_c: u32, _k: u32) -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
@@ -177,6 +177,30 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
fn update_bond(c: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `306 + c * (50 ±0)`
// Estimated: `6287`
// Minimum execution time: 34_814_000 picoseconds.
Weight::from_parts(36_371_520, 0)
.saturating_add(Weight::from_parts(0, 6287))
// Standard Error: 2_391
.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
fn take_candidate_slot(c: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `306 + c * (50 ±0)`
// Estimated: `6287`
// Minimum execution time: 34_814_000 picoseconds.
Weight::from_parts(36_371_520, 0)
.saturating_add(Weight::from_parts(0, 6287))
// Standard Error: 2_391
.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
/// Storage: `System::Account` (r:2 w:2)
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
/// Storage: `System::BlockWeight` (r:1 w:1)
@@ -123,7 +123,7 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
}
/// Storage: `CollatorSelection::CandidacyBond` (r:0 w:1)
/// Proof: `CollatorSelection::CandidacyBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`)
fn set_candidacy_bond() -> Weight {
fn set_candidacy_bond(_c: u32, _k: u32) -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
@@ -177,6 +177,30 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
fn update_bond(c: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `306 + c * (50 ±0)`
// Estimated: `6287`
// Minimum execution time: 34_814_000 picoseconds.
Weight::from_parts(36_371_520, 0)
.saturating_add(Weight::from_parts(0, 6287))
// Standard Error: 2_391
.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
fn take_candidate_slot(c: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `306 + c * (50 ±0)`
// Estimated: `6287`
// Minimum execution time: 34_814_000 picoseconds.
Weight::from_parts(36_371_520, 0)
.saturating_add(Weight::from_parts(0, 6287))
// Standard Error: 2_391
.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
/// Storage: `System::Account` (r:2 w:2)
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
/// Storage: `System::BlockWeight` (r:1 w:1)
@@ -123,7 +123,7 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
}
/// Storage: `CollatorSelection::CandidacyBond` (r:0 w:1)
/// Proof: `CollatorSelection::CandidacyBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`)
fn set_candidacy_bond() -> Weight {
fn set_candidacy_bond(_c: u32, _k: u32) -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
@@ -177,6 +177,30 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
fn update_bond(c: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `306 + c * (50 ±0)`
// Estimated: `6287`
// Minimum execution time: 34_814_000 picoseconds.
Weight::from_parts(36_371_520, 0)
.saturating_add(Weight::from_parts(0, 6287))
// Standard Error: 2_391
.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
fn take_candidate_slot(c: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `306 + c * (50 ±0)`
// Estimated: `6287`
// Minimum execution time: 34_814_000 picoseconds.
Weight::from_parts(36_371_520, 0)
.saturating_add(Weight::from_parts(0, 6287))
// Standard Error: 2_391
.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
/// Storage: `System::Account` (r:2 w:2)
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
/// Storage: `System::BlockWeight` (r:1 w:1)
@@ -124,7 +124,7 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
}
/// Storage: `CollatorSelection::CandidacyBond` (r:0 w:1)
/// Proof: `CollatorSelection::CandidacyBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`)
fn set_candidacy_bond() -> Weight {
fn set_candidacy_bond(_c: u32, _k: u32) -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
@@ -178,6 +178,30 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
fn update_bond(c: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `306 + c * (50 ±0)`
// Estimated: `6287`
// Minimum execution time: 34_814_000 picoseconds.
Weight::from_parts(36_371_520, 0)
.saturating_add(Weight::from_parts(0, 6287))
// Standard Error: 2_391
.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
fn take_candidate_slot(c: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `306 + c * (50 ±0)`
// Estimated: `6287`
// Minimum execution time: 34_814_000 picoseconds.
Weight::from_parts(36_371_520, 0)
.saturating_add(Weight::from_parts(0, 6287))
// Standard Error: 2_391
.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
/// Storage: `System::Account` (r:2 w:2)
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
/// Storage: `System::BlockWeight` (r:1 w:1)
@@ -121,7 +121,7 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
}
/// Storage: `CollatorSelection::CandidacyBond` (r:0 w:1)
/// Proof: `CollatorSelection::CandidacyBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`)
fn set_candidacy_bond() -> Weight {
fn set_candidacy_bond(_c: u32, _k: u32) -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
@@ -175,6 +175,30 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
fn update_bond(c: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `306 + c * (50 ±0)`
// Estimated: `6287`
// Minimum execution time: 34_814_000 picoseconds.
Weight::from_parts(36_371_520, 0)
.saturating_add(Weight::from_parts(0, 6287))
// Standard Error: 2_391
.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
fn take_candidate_slot(c: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `306 + c * (50 ±0)`
// Estimated: `6287`
// Minimum execution time: 34_814_000 picoseconds.
Weight::from_parts(36_371_520, 0)
.saturating_add(Weight::from_parts(0, 6287))
// Standard Error: 2_391
.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
/// Storage: `System::Account` (r:2 w:2)
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
/// Storage: `System::BlockWeight` (r:1 w:1)