Newer
Older
<Ledger<T>>::insert(
voter.clone(),
StakingLedger {
stash: voter.clone(),
active: stake,
total: stake,
unlocking: Default::default(),
},
);
Self::do_add_nominator(&voter, Nominations { targets, submitted_in: 0, suppressed: false });
}
#[cfg(feature = "runtime-benchmarks")]
fn add_target(target: T::AccountId) {
let stake = MinValidatorBond::<T>::get() * 100u32.into();
<Bonded<T>>::insert(target.clone(), target.clone());
<Ledger<T>>::insert(
target.clone(),
StakingLedger {
stash: target.clone(),
active: stake,
total: stake,
unlocking: Default::default(),
},
);
Self::do_add_validator(
&target,
ValidatorPrefs { commission: Perbill::zero(), blocked: false },
);
}
#[cfg(feature = "runtime-benchmarks")]
fn clear() {
<Bonded<T>>::remove_all(None);
<Ledger<T>>::remove_all(None);
<Validators<T>>::remove_all();
<Nominators<T>>::remove_all();
Kian Paimani
committed
T::VoterList::unsafe_clear();
#[cfg(feature = "runtime-benchmarks")]
fn put_snapshot(
voters: Vec<VoterOf<Self>>,
targets: Vec<T::AccountId>,
target_stake: Option<VoteWeight>,
) {
targets.into_iter().for_each(|v| {
let stake: BalanceOf<T> = target_stake
.and_then(|w| <BalanceOf<T>>::try_from(w).ok())
.unwrap_or_else(|| MinNominatorBond::<T>::get() * 100u32.into());
<Bonded<T>>::insert(v.clone(), v.clone());
<Ledger<T>>::insert(
v.clone(),
StakingLedger {
stash: v.clone(),
active: stake,
total: stake,
unlocking: Default::default(),
},
);
Self::do_add_validator(
&v,
ValidatorPrefs { commission: Perbill::zero(), blocked: false },
);
});
voters.into_iter().for_each(|(v, s, t)| {
let stake = <BalanceOf<T>>::try_from(s).unwrap_or_else(|_| {
panic!("cannot convert a VoteWeight into BalanceOf, benchmark needs reconfiguring.")
});
<Bonded<T>>::insert(v.clone(), v.clone());
<Ledger<T>>::insert(
v.clone(),
StakingLedger {
stash: v.clone(),
active: stake,
total: stake,
unlocking: Default::default(),
},
);
Self::do_add_nominator(
&v,
Nominations { targets: t, submitted_in: 0, suppressed: false },
);
});
}
}
/// In this implementation `new_session(session)` must be called before `end_session(session-1)`
/// i.e. the new session must be planned before the ending of the previous session.
///
/// Once the first new_session is planned, all session must start and then end in order, though
/// some session can lag in between the newest session planned and the latest session started.
impl<T: Config> pallet_session::SessionManager<T::AccountId> for Pallet<T> {
fn new_session(new_index: SessionIndex) -> Option<Vec<T::AccountId>> {
log!(trace, "planning new session {}", new_index);
CurrentPlannedSession::<T>::put(new_index);
Self::new_session(new_index, false).map(|v| v.into_inner())
}
fn new_session_genesis(new_index: SessionIndex) -> Option<Vec<T::AccountId>> {
log!(trace, "planning new session {} at genesis", new_index);
CurrentPlannedSession::<T>::put(new_index);
Self::new_session(new_index, true).map(|v| v.into_inner())
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
}
fn start_session(start_index: SessionIndex) {
log!(trace, "starting session {}", start_index);
Self::start_session(start_index)
}
fn end_session(end_index: SessionIndex) {
log!(trace, "ending session {}", end_index);
Self::end_session(end_index)
}
}
impl<T: Config> historical::SessionManager<T::AccountId, Exposure<T::AccountId, BalanceOf<T>>>
for Pallet<T>
{
fn new_session(
new_index: SessionIndex,
) -> Option<Vec<(T::AccountId, Exposure<T::AccountId, BalanceOf<T>>)>> {
<Self as pallet_session::SessionManager<_>>::new_session(new_index).map(|validators| {
let current_era = Self::current_era()
// Must be some as a new era has been created.
.unwrap_or(0);
validators
.into_iter()
.map(|v| {
let exposure = Self::eras_stakers(current_era, &v);
(v, exposure)
})
.collect()
})
}
fn new_session_genesis(
new_index: SessionIndex,
) -> Option<Vec<(T::AccountId, Exposure<T::AccountId, BalanceOf<T>>)>> {
<Self as pallet_session::SessionManager<_>>::new_session_genesis(new_index).map(
|validators| {
let current_era = Self::current_era()
// Must be some as a new era has been created.
.unwrap_or(0);
validators
.into_iter()
.map(|v| {
let exposure = Self::eras_stakers(current_era, &v);
(v, exposure)
})
.collect()
},
)
}
fn start_session(start_index: SessionIndex) {
<Self as pallet_session::SessionManager<_>>::start_session(start_index)
}
fn end_session(end_index: SessionIndex) {
<Self as pallet_session::SessionManager<_>>::end_session(end_index)
}
}
/// Add reward points to block authors:
/// * 20 points to the block producer for producing a (non-uncle) block in the relay chain,
/// * 2 points to the block producer for each reference to a previously unreferenced uncle, and
/// * 1 point to the producer of each referenced uncle block.
impl<T> pallet_authorship::EventHandler<T::AccountId, T::BlockNumber> for Pallet<T>
where
T: Config + pallet_authorship::Config + pallet_session::Config,
{
fn note_author(author: T::AccountId) {
Self::reward_by_ids(vec![(author, 20)])
}
fn note_uncle(uncle_author: T::AccountId, _age: T::BlockNumber) {
// defensive-only: block author must exist.
if let Some(block_author) = <pallet_authorship::Pallet<T>>::author() {
Self::reward_by_ids(vec![(block_author, 2), (uncle_author, 1)])
} else {
crate::log!(warn, "block author not set, this should never happen");
}
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
}
}
/// This is intended to be used with `FilterHistoricalOffences`.
impl<T: Config>
OnOffenceHandler<T::AccountId, pallet_session::historical::IdentificationTuple<T>, Weight>
for Pallet<T>
where
T: pallet_session::Config<ValidatorId = <T as frame_system::Config>::AccountId>,
T: pallet_session::historical::Config<
FullIdentification = Exposure<<T as frame_system::Config>::AccountId, BalanceOf<T>>,
FullIdentificationOf = ExposureOf<T>,
>,
T::SessionHandler: pallet_session::SessionHandler<<T as frame_system::Config>::AccountId>,
T::SessionManager: pallet_session::SessionManager<<T as frame_system::Config>::AccountId>,
T::ValidatorIdOf: Convert<
<T as frame_system::Config>::AccountId,
Option<<T as frame_system::Config>::AccountId>,
>,
{
fn on_offence(
offenders: &[OffenceDetails<
T::AccountId,
pallet_session::historical::IdentificationTuple<T>,
>],
slash_fraction: &[Perbill],
slash_session: SessionIndex,
disable_strategy: DisableStrategy,
) -> Weight {
let reward_proportion = SlashRewardFraction::<T>::get();
let mut consumed_weight = Weight::from_ref_time(0);
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
let mut add_db_reads_writes = |reads, writes| {
consumed_weight += T::DbWeight::get().reads_writes(reads, writes);
};
let active_era = {
let active_era = Self::active_era();
add_db_reads_writes(1, 0);
if active_era.is_none() {
// This offence need not be re-submitted.
return consumed_weight
}
active_era.expect("value checked not to be `None`; qed").index
};
let active_era_start_session_index = Self::eras_start_session_index(active_era)
.unwrap_or_else(|| {
frame_support::print("Error: start_session_index must be set for current_era");
0
});
add_db_reads_writes(1, 0);
let window_start = active_era.saturating_sub(T::BondingDuration::get());
// Fast path for active-era report - most likely.
// `slash_session` cannot be in a future active era. It must be in `active_era` or before.
let slash_era = if slash_session >= active_era_start_session_index {
active_era
} else {
let eras = BondedEras::<T>::get();
add_db_reads_writes(1, 0);
// Reverse because it's more likely to find reports from recent eras.
match eras.iter().rev().find(|&&(_, ref sesh)| sesh <= &slash_session) {
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
Some(&(ref slash_era, _)) => *slash_era,
// Before bonding period. defensive - should be filtered out.
None => return consumed_weight,
}
};
add_db_reads_writes(1, 1);
let slash_defer_duration = T::SlashDeferDuration::get();
let invulnerables = Self::invulnerables();
add_db_reads_writes(1, 0);
for (details, slash_fraction) in offenders.iter().zip(slash_fraction) {
let (stash, exposure) = &details.offender;
// Skip if the validator is invulnerable.
if invulnerables.contains(stash) {
continue
}
let unapplied = slashing::compute_slash::<T>(slashing::SlashParams {
stash,
slash: *slash_fraction,
exposure,
slash_era,
window_start,
now: active_era,
reward_proportion,
disable_strategy,
});
Self::deposit_event(Event::<T>::SlashReported {
validator: stash.clone(),
fraction: *slash_fraction,
slash_era,
});
if let Some(mut unapplied) = unapplied {
let nominators_len = unapplied.others.len() as u64;
let reporters_len = details.reporters.len() as u64;
{
let upper_bound = 1 /* Validator/NominatorSlashInEra */ + 2 /* fetch_spans */;
let rw = upper_bound + nominators_len * upper_bound;
add_db_reads_writes(rw, rw);
}
unapplied.reporters = details.reporters.clone();
if slash_defer_duration == 0 {
// Apply right away.
slashing::apply_slash::<T>(unapplied, slash_era);
{
let slash_cost = (6, 5);
let reward_cost = (2, 2);
add_db_reads_writes(
(1 + nominators_len) * slash_cost.0 + reward_cost.0 * reporters_len,
(1 + nominators_len) * slash_cost.1 + reward_cost.1 * reporters_len,
);
}
} else {
// Defer to end of some `slash_defer_duration` from now.
log!(
debug,
"deferring slash of {:?}% happened in {:?} (reported in {:?}) to {:?}",
slash_fraction,
slash_era,
active_era,
slash_era + slash_defer_duration + 1,
);
<Self as Store>::UnappliedSlashes::mutate(
slash_era.saturating_add(slash_defer_duration).saturating_add(One::one()),
move |for_later| for_later.push(unapplied),
);
add_db_reads_writes(1, 1);
}
} else {
add_db_reads_writes(4 /* fetch_spans */, 5 /* kick_out_if_recent */)
}
}
consumed_weight
}
}
impl<T: Config> ScoreProvider<T::AccountId> for Pallet<T> {
type Score = VoteWeight;
fn score(who: &T::AccountId) -> Self::Score {
Self::weight_of(who)
}
#[cfg(feature = "runtime-benchmarks")]
fn set_score_of(who: &T::AccountId, weight: Self::Score) {
// this will clearly results in an inconsistent state, but it should not matter for a
// benchmark.
let active: BalanceOf<T> = weight.try_into().map_err(|_| ()).unwrap();
let mut ledger = match Self::ledger(who) {
None => StakingLedger::default_from(who.clone()),
ledger.active = active;
<Ledger<T>>::insert(who, ledger);
<Bonded<T>>::insert(who, who);
// also, we play a trick to make sure that a issuance based-`CurrencyToVote` behaves well:
// This will make sure that total issuance is zero, thus the currency to vote will be a 1-1
// conversion.
let imbalance = T::Currency::burn(T::Currency::total_issuance());
// kinda ugly, but gets the job done. The fact that this works here is a HUGE exception.
// Don't try this pattern in other places.
sp_std::mem::forget(imbalance);
}
}
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
/// A simple sorted list implementation that does not require any additional pallets. Note, this
/// does not provide validators in sorted order. If you desire nominators in a sorted order take
/// a look at [`pallet-bags-list`].
pub struct UseValidatorsMap<T>(sp_std::marker::PhantomData<T>);
impl<T: Config> SortedListProvider<T::AccountId> for UseValidatorsMap<T> {
type Score = BalanceOf<T>;
type Error = ();
/// Returns iterator over voter list, which can have `take` called on it.
fn iter() -> Box<dyn Iterator<Item = T::AccountId>> {
Box::new(Validators::<T>::iter().map(|(v, _)| v))
}
fn iter_from(
start: &T::AccountId,
) -> Result<Box<dyn Iterator<Item = T::AccountId>>, Self::Error> {
if Validators::<T>::contains_key(start) {
let start_key = Validators::<T>::hashed_key_for(start);
Ok(Box::new(Validators::<T>::iter_from(start_key).map(|(n, _)| n)))
} else {
Err(())
}
}
fn count() -> u32 {
Validators::<T>::count()
}
fn contains(id: &T::AccountId) -> bool {
Validators::<T>::contains_key(id)
}
fn on_insert(_: T::AccountId, _weight: Self::Score) -> Result<(), Self::Error> {
// nothing to do on insert.
Ok(())
}
fn get_score(id: &T::AccountId) -> Result<Self::Score, Self::Error> {
Ok(Pallet::<T>::weight_of(id).into())
}
fn on_update(_: &T::AccountId, _weight: Self::Score) -> Result<(), Self::Error> {
// nothing to do on update.
Ok(())
}
fn on_remove(_: &T::AccountId) -> Result<(), Self::Error> {
// nothing to do on remove.
Ok(())
}
fn unsafe_regenerate(
_: impl IntoIterator<Item = T::AccountId>,
_: Box<dyn Fn(&T::AccountId) -> Self::Score>,
) -> u32 {
// nothing to do upon regenerate.
0
}
fn try_state() -> Result<(), &'static str> {
Ok(())
}
fn unsafe_clear() {
#[allow(deprecated)]
Validators::<T>::remove_all();
}
#[cfg(feature = "runtime-benchmarks")]
fn score_update_worst_case(_who: &T::AccountId, _is_increase: bool) -> Self::Score {
unimplemented!()
}
/// A simple voter list implementation that does not require any additional pallets. Note, this
/// does not provided nominators in sorted ordered. If you desire nominators in a sorted order take
/// a look at [`pallet-bags-list].
Kian Paimani
committed
pub struct UseNominatorsAndValidatorsMap<T>(sp_std::marker::PhantomData<T>);
impl<T: Config> SortedListProvider<T::AccountId> for UseNominatorsAndValidatorsMap<T> {
type Error = ();
type Score = VoteWeight;
fn iter() -> Box<dyn Iterator<Item = T::AccountId>> {
Kian Paimani
committed
Box::new(
Validators::<T>::iter()
.map(|(v, _)| v)
.chain(Nominators::<T>::iter().map(|(n, _)| n)),
)
}
fn iter_from(
start: &T::AccountId,
) -> Result<Box<dyn Iterator<Item = T::AccountId>>, Self::Error> {
if Validators::<T>::contains_key(start) {
let start_key = Validators::<T>::hashed_key_for(start);
Ok(Box::new(
Validators::<T>::iter_from(start_key)
.map(|(n, _)| n)
.chain(Nominators::<T>::iter().map(|(x, _)| x)),
))
} else if Nominators::<T>::contains_key(start) {
let start_key = Nominators::<T>::hashed_key_for(start);
Ok(Box::new(Nominators::<T>::iter_from(start_key).map(|(n, _)| n)))
} else {
Err(())
}
}
fn count() -> u32 {
Kian Paimani
committed
Nominators::<T>::count().saturating_add(Validators::<T>::count())
}
fn contains(id: &T::AccountId) -> bool {
Kian Paimani
committed
Nominators::<T>::contains_key(id) || Validators::<T>::contains_key(id)
}
fn on_insert(_: T::AccountId, _weight: Self::Score) -> Result<(), Self::Error> {
// nothing to do on insert.
Ok(())
}
fn get_score(id: &T::AccountId) -> Result<Self::Score, Self::Error> {
Ok(Pallet::<T>::weight_of(id))
}
fn on_update(_: &T::AccountId, _weight: Self::Score) -> Result<(), Self::Error> {
// nothing to do on update.
}
fn on_remove(_: &T::AccountId) -> Result<(), Self::Error> {
// nothing to do on remove.
}
_: impl IntoIterator<Item = T::AccountId>,
_: Box<dyn Fn(&T::AccountId) -> Self::Score>,
) -> u32 {
// nothing to do upon regenerate.
0
}
fn try_state() -> Result<(), &'static str> {
Ok(())
}
fn unsafe_clear() {
// NOTE: Caller must ensure this doesn't lead to too many storage accesses. This is a
// condition of SortedListProvider::unsafe_clear.
Nominators::<T>::remove_all();
Kian Paimani
committed
Validators::<T>::remove_all();
}
#[cfg(feature = "runtime-benchmarks")]
fn score_update_worst_case(_who: &T::AccountId, _is_increase: bool) -> Self::Score {
unimplemented!()
}
}
![Roman Useinov Roman Useinov's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Roman Useinov
committed
// NOTE: in this entire impl block, the assumption is that `who` is a stash account.
impl<T: Config> StakingInterface for Pallet<T> {
type AccountId = T::AccountId;
type Balance = BalanceOf<T>;
![Roman Useinov Roman Useinov's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Roman Useinov
committed
fn minimum_nominator_bond() -> Self::Balance {
![Roman Useinov Roman Useinov's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Roman Useinov
committed
fn minimum_validator_bond() -> Self::Balance {
MinValidatorBond::<T>::get()
}
fn desired_validator_count() -> u32 {
ValidatorCount::<T>::get()
}
fn election_ongoing() -> bool {
T::ElectionProvider::ongoing()
![Roman Useinov Roman Useinov's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Roman Useinov
committed
}
fn force_unstake(who: Self::AccountId) -> sp_runtime::DispatchResult {
let num_slashing_spans = Self::slashing_spans(&who).iter().count() as u32;
Self::force_unstake(RawOrigin::Root.into(), who.clone(), num_slashing_spans)
}
fn stash_by_ctrl(controller: &Self::AccountId) -> Result<Self::AccountId, DispatchError> {
Self::ledger(controller)
.map(|l| l.stash)
.ok_or(Error::<T>::NotController.into())
}
fn is_exposed_in_era(who: &Self::AccountId, era: &EraIndex) -> bool {
ErasStakers::<T>::iter_prefix(era).any(|(validator, exposures)| {
validator == *who || exposures.others.iter().any(|i| i.who == *who)
})
}
fn bonding_duration() -> EraIndex {
T::BondingDuration::get()
}
fn current_era() -> EraIndex {
Self::current_era().unwrap_or(Zero::zero())
}
![Roman Useinov Roman Useinov's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Roman Useinov
committed
fn stake(who: &Self::AccountId) -> Result<Stake<Self>, DispatchError> {
Self::bonded(who)
.and_then(|c| Self::ledger(c))
.map(|l| Stake { stash: l.stash, total: l.total, active: l.active })
.ok_or(Error::<T>::NotStash.into())
![Roman Useinov Roman Useinov's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Roman Useinov
committed
fn bond_extra(who: &Self::AccountId, extra: Self::Balance) -> DispatchResult {
Self::bond_extra(RawOrigin::Signed(who.clone()).into(), extra)
![Roman Useinov Roman Useinov's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Roman Useinov
committed
fn unbond(who: &Self::AccountId, value: Self::Balance) -> DispatchResult {
let ctrl = Self::bonded(who).ok_or(Error::<T>::NotStash)?;
Self::unbond(RawOrigin::Signed(ctrl).into(), value)
![Roman Useinov Roman Useinov's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Roman Useinov
committed
fn chill(who: &Self::AccountId) -> DispatchResult {
// defensive-only: any account bonded via this interface has the stash set as the
// controller, but we have to be sure. Same comment anywhere else that we read this.
let ctrl = Self::bonded(who).ok_or(Error::<T>::NotStash)?;
Self::chill(RawOrigin::Signed(ctrl).into())
}
![Roman Useinov Roman Useinov's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Roman Useinov
committed
who: Self::AccountId,
) -> Result<bool, DispatchError> {
![Roman Useinov Roman Useinov's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Roman Useinov
committed
let ctrl = Self::bonded(who).ok_or(Error::<T>::NotStash)?;
Self::withdraw_unbonded(RawOrigin::Signed(ctrl.clone()).into(), num_slashing_spans)
.map(|_| !Ledger::<T>::contains_key(&ctrl))
.map_err(|with_post| with_post.error)
![Roman Useinov Roman Useinov's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Roman Useinov
committed
who: &Self::AccountId,
![Roman Useinov Roman Useinov's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Roman Useinov
committed
payee: &Self::AccountId,
![Roman Useinov Roman Useinov's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Roman Useinov
committed
RawOrigin::Signed(who.clone()).into(),
T::Lookup::unlookup(who.clone()),
![Roman Useinov Roman Useinov's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Roman Useinov
committed
RewardDestination::Account(payee.clone()),
![Roman Useinov Roman Useinov's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Roman Useinov
committed
fn nominate(who: &Self::AccountId, targets: Vec<Self::AccountId>) -> DispatchResult {
let ctrl = Self::bonded(who).ok_or(Error::<T>::NotStash)?;
let targets = targets.into_iter().map(T::Lookup::unlookup).collect::<Vec<_>>();
![Roman Useinov Roman Useinov's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Roman Useinov
committed
Self::nominate(RawOrigin::Signed(ctrl).into(), targets)
sp_staking::runtime_benchmarks_enabled! {
fn nominations(who: Self::AccountId) -> Option<Vec<T::AccountId>> {
Nominators::<T>::get(who).map(|n| n.targets.into_inner())
}
![Roman Useinov Roman Useinov's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Roman Useinov
committed
fn add_era_stakers(
current_era: &EraIndex,
stash: &T::AccountId,
exposures: Vec<(Self::AccountId, Self::Balance)>,
) {
let others = exposures
.iter()
.map(|(who, value)| IndividualExposure { who: who.clone(), value: value.clone() })
.collect::<Vec<_>>();
let exposure = Exposure { total: Default::default(), own: Default::default(), others };
<ErasStakers<T>>::insert(¤t_era, &stash, &exposure);
}
![Roman Useinov Roman Useinov's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Roman Useinov
committed
fn set_current_era(era: EraIndex) {
CurrentEra::<T>::put(era);
}
![Roman Useinov Roman Useinov's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Roman Useinov
committed
}
#[cfg(any(test, feature = "try-runtime"))]
impl<T: Config> Pallet<T> {
pub(crate) fn do_try_state(_: BlockNumberFor<T>) -> Result<(), &'static str> {
ensure!(
T::VoterList::iter()
.all(|x| <Nominators<T>>::contains_key(&x) || <Validators<T>>::contains_key(&x)),
"VoterList contains non-nominators"
);
T::VoterList::try_state()?;
Self::check_nominators()?;
Self::check_exposures()?;
Self::check_ledgers()?;
Self::check_count()
}
fn check_count() -> Result<(), &'static str> {
ensure!(
<T as Config>::VoterList::count() ==
Nominators::<T>::count() + Validators::<T>::count(),
"wrong external count"
);
ensure!(
ValidatorCount::<T>::get() <=
<T::ElectionProvider as frame_election_provider_support::ElectionProviderBase>::MaxWinners::get(),
"validator count exceeded election max winners"
);
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
Ok(())
}
fn check_ledgers() -> Result<(), &'static str> {
Bonded::<T>::iter()
.map(|(_, ctrl)| Self::ensure_ledger_consistent(ctrl))
.collect::<Result<_, _>>()
}
fn check_exposures() -> Result<(), &'static str> {
// a check per validator to ensure the exposure struct is always sane.
let era = Self::active_era().unwrap().index;
ErasStakers::<T>::iter_prefix_values(era)
.map(|expo| {
ensure!(
expo.total ==
expo.own +
expo.others
.iter()
.map(|e| e.value)
.fold(Zero::zero(), |acc, x| acc + x),
"wrong total exposure.",
);
Ok(())
})
.collect::<Result<_, _>>()
}
fn check_nominators() -> Result<(), &'static str> {
// a check per nominator to ensure their entire stake is correctly distributed. Will only
// kick-in if the nomination was submitted before the current era.
let era = Self::active_era().unwrap().index;
<Nominators<T>>::iter()
.filter_map(
|(nominator, nomination)| {
if nomination.submitted_in > era {
Some(nominator)
} else {
None
}
},
)
.map(|nominator| {
// must be bonded.
Self::ensure_is_stash(&nominator)?;
let mut sum = BalanceOf::<T>::zero();
T::SessionInterface::validators()
.iter()
.map(|v| Self::eras_stakers(era, v))
.map(|e| {
let individual =
e.others.iter().filter(|e| e.who == nominator).collect::<Vec<_>>();
let len = individual.len();
match len {
0 => { /* not supporting this validator at all. */ },
1 => sum += individual[0].value,
_ => return Err("nominator cannot back a validator more than once."),
};
Ok(())
})
.collect::<Result<_, _>>()
})
.collect::<Result<_, _>>()
}
fn ensure_is_stash(who: &T::AccountId) -> Result<(), &'static str> {
ensure!(Self::bonded(who).is_some(), "Not a stash.");
Ok(())
}
fn ensure_ledger_consistent(ctrl: T::AccountId) -> Result<(), &'static str> {
// ensures ledger.total == ledger.active + sum(ledger.unlocking).
let ledger = Self::ledger(ctrl.clone()).ok_or("Not a controller.")?;
let real_total: BalanceOf<T> =
ledger.unlocking.iter().fold(ledger.active, |a, c| a + c.value);
ensure!(real_total == ledger.total, "ledger.total corrupt");
if !(ledger.active >= T::Currency::minimum_balance() || ledger.active.is_zero()) {
log!(warn, "ledger.active less than ED: {:?}, {:?}", ctrl, ledger)
}
Ok(())
}
}