-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Stash/controller model for staking #1782
Changes from 1 commit
e89dff9
8e9c2d5
29d1b20
ac5d897
bfc089f
fe19f73
24cedfb
0876c86
c9f643f
9190f7d
4b5274e
5354a2e
00ba07e
78bcf1a
5ec3741
ca18c79
a24dd8a
15a3100
bd0a3de
68f5405
001a665
7042842
75bf9d7
f4ba1d6
a008ee1
27ad50e
93d0eb0
9f7ba25
b755466
50a212c
1eb43f7
6eaddd1
e8661b2
7284530
7e9d469
31bed92
cadce5e
37adc08
039f4ea
4e67748
7fe7e01
a911ee9
514d5a6
1808869
2c4f738
a163f87
1296562
200eea1
15b9da0
634ab7b
94a46d4
1a2ec9e
dd7fba6
4b0e1e6
61224f6
e4ea05f
3b9917a
6f8f93d
4029139
cc8f195
b0129a0
92c0621
16a9239
1064cfc
6865917
937c89c
f606db2
6bf11f9
9a02201
be9f4f6
4ddaa42
54ee55b
a53c3fd
93da8d8
4603cfe
8bb2e43
2f43287
a4ca045
ee54da0
51d25ed
231a02d
68b5938
3a74b73
ae1e1e5
da9cb52
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -112,18 +112,6 @@ pub struct Exposure<AccountId, Balance> { | |
| pub others: Vec<(AccountId, Balance)>, | ||
| } | ||
|
|
||
| /// Capacity in which a bonded account is participating. | ||
| #[derive(PartialEq, Eq, Clone, Encode, Decode)] | ||
| #[cfg_attr(feature = "std", derive(Debug))] | ||
| pub enum Capacity<AccountId> { | ||
| /// Do nothing. | ||
| Idle, | ||
| /// We are nominating. | ||
| Nominator(Vec<AccountId>), | ||
| /// We want to be a validator. | ||
| Validator, | ||
| } | ||
|
|
||
| impl<AccountId> Default for Capacity<AccountId> { | ||
| fn default() -> Self { | ||
| Capacity::Nominator(vec![]) | ||
|
|
@@ -173,13 +161,17 @@ decl_storage! { | |
| /// Map from all (unlocked) "controller" accounts to the info regarding the staking. | ||
| pub Ledger get(ledger): map T::AccountId => Option<StakingLedger<T::AccountId, BalanceOf<T>, T::BlockNumber>>; | ||
|
|
||
| /// Preferences that a validator has. | ||
| pub ValidatorPreferences get(validator_preferences): map T::AccountId => ValidatorPrefs<BalanceOf<T>>; | ||
|
|
||
| //============================================================================================================================== | ||
|
|
||
| // The desired capacity in which a controller wants to operate. | ||
| pub Desired get(desired): linked_map AccountId => Capacity<T::AccountId>; | ||
| /// The set of keys are all controllers that want to validate. | ||
| /// | ||
| /// The value are the preferences that a validator has. | ||
| pub Validators get(validators): linked_map T::AccountId => ValidatorPrefs<BalanceOf<T>>; | ||
|
|
||
| /// The set of keys are all controllers that want to nominate. | ||
| /// | ||
| /// The value are the nominations. | ||
| pub Nominators get(nominators): linked_map AccountId => Vec<T::AccountId>; | ||
|
|
||
| /// Nominators for a particular account that is in action right now. You can't iterate through validators here, | ||
| /// but you can find them in the `sessions` module. | ||
|
|
@@ -322,10 +314,11 @@ decl_module! { | |
| /// Declare the desire to validate for the origin controller. | ||
| /// | ||
| /// Effects will be felt at the beginning of the next era. | ||
| fn validate(origin) { | ||
| fn validate(origin, prefs: ValidatorPrefs) { | ||
| let controller = ensure_signed(origin)?; | ||
| let mut _ledger = Self::ledger(&controller).ok_or("not a controller")?; | ||
| Desired<T>::insert(controller, Capacity::Validator); | ||
| Nominators<T>::remove(&controller); | ||
| Validators<T>::insert(controller, prefs); | ||
| } | ||
|
|
||
| /// Declare the desire to nominate `targets` for the origin controller. | ||
|
|
@@ -334,12 +327,14 @@ decl_module! { | |
| fn nominate(origin, targets: Vec<<T::Lookup as StaticLookup>::Source>) { | ||
| let controller = ensure_signed(origin)?; | ||
| let mut _ledger = Self::ledger(&controller).ok_or("not a controller")?; | ||
| ensure!(!targets.is_empty(), "targets cannot be empty"); | ||
| let targets = target.into_iter() | ||
| .take(MAX_NOMINATIONS) | ||
| .map(T::Lookup::lookup) | ||
| .collect::<Result<Vec<T::AccountId>, &'static str>>()?; | ||
|
|
||
| Desired<T>::insert(controller, Capacity::Nominator(targets)); | ||
| Validators<T>::remove(&controller); | ||
| Nominators<T>::insert(controller, targets); | ||
| } | ||
|
|
||
| /// Declare no desire to either validate or nominate. | ||
|
|
@@ -348,21 +343,8 @@ decl_module! { | |
| fn chill(origin) { | ||
| let controller = ensure_signed(origin)?; | ||
| let mut _ledger = Self::ledger(&controller).ok_or("not a controller")?; | ||
| Desired<T>::insert(controller, Capacity::Idle); | ||
| } | ||
|
|
||
| /// Set the given account's preference for slashing behaviour should they be a validator. | ||
| /// | ||
| /// An error (no-op) if `Self::intentions()[intentions_index] != origin`. | ||
| fn register_preferences( | ||
| origin, | ||
| #[compact] intentions_index: u32, | ||
| prefs: ValidatorPrefs<BalanceOf<T>> | ||
| ) { | ||
| let controller = ensure_signed(origin)?; | ||
| let mut _ledger = Self::ledger(&controller).ok_or("not a controller")?; | ||
|
|
||
| <ValidatorPreferences<T>>::insert(controller, prefs); | ||
| Validators<T>::remove(&controller); | ||
| Nominators<T>::remove(&controller); | ||
| } | ||
|
|
||
| /// Set the number of sessions in an era. | ||
|
|
@@ -482,7 +464,7 @@ impl<T: Trait> Module<T> { | |
| /// Reward a given validator by a specific amount. Add the reward to their, and their nominators' | ||
| /// balance, pro-rata based on their exposure, after having removed the validator's pre-payout cut. | ||
| fn reward_validator(who: &T::AccountId, reward: BalanceOf<T>) { | ||
| let off_the_table = reward.min(Self::validator_preferences(who).validator_payment); | ||
| let off_the_table = reward.min(Self::validators(who).validator_payment); | ||
| let reward = reward - off_the_table; | ||
| let validator_cut = if reward.is_zero() { | ||
| Zero::zero() | ||
|
|
@@ -498,12 +480,6 @@ impl<T: Trait> Module<T> { | |
| let _ = T::Currency::reward(who, validator_cut + off_the_table); | ||
| } | ||
|
|
||
| /// Actually carry out the unvalidate operation. | ||
| /// Assumes `intentions()[intentions_index] == who`. | ||
| fn apply_unvalidate(who: &T::AccountId) { | ||
| <Desired<T>>::insert(who, Capacity::Idle); | ||
| } | ||
|
|
||
| /// Get the reward for the session, assuming it ends with this block. | ||
| fn this_session_reward(actual_elapsed: T::Moment) -> BalanceOf<T> { | ||
| let ideal_elapsed = <session::Module<T>>::ideal_session_duration(); | ||
|
|
@@ -564,36 +540,27 @@ impl<T: Trait> Module<T> { | |
|
|
||
| // TODO: Can probably pre-process a lot of complexity out of these two for-loops, | ||
| // particularly the need to keep everything in a BTreeMap. | ||
| // May be reasonable to split the two tries Validator | ||
|
|
||
| // Map of (would-be) validator account to amount of stake backing it. | ||
| let mut candidates: Vec<(T::AccountId, Exposure)> = Default::default(); | ||
| for (who, capacity) in <Desired<T>>::enumerate() { | ||
| match capacity { | ||
| Validator => { | ||
| let stash_balance = Self::stash_balance(&who); | ||
| candidates.push((who, Exposure { total: stash_balance, own: stash_balance, others: vec![] })); | ||
| } | ||
| _ => {} | ||
| } | ||
| for (who, _) in <Validators<T>>::enumerate() { | ||
| let stash_balance = Self::stash_balance(&who); | ||
| candidates.push((who, Exposure { total: stash_balance, own: stash_balance, others: vec![] })); | ||
| } | ||
| canidates.sort_unstable_by_key(|i| &i.0); | ||
| for (who, capacity) in <Desired<T>>::enumerate() { | ||
| match capacity { | ||
| Nominator(nominees) => { | ||
| // For this trivial nominator mapping, we just assume that nominators always | ||
| // have themselves assigned to the first validator in their list. | ||
| let chosen = if nominees.len() > 0 { | ||
| candidates[0] | ||
| } else { | ||
| continue | ||
| }; | ||
| if let Ok(index) = candidates.binary_search_by_key(&who, |i| &i.0) { | ||
| let stash_balance = Self::stash_balance(&who); | ||
| candidates[index].1.total += stash_balance; | ||
| candidates[index].1.others.push((who, stash_balance)); | ||
| } | ||
| } | ||
| _ => {} | ||
| for (who, nominees) in <Nominators<T>>::enumerate() { | ||
| // For this trivial nominator mapping, we just assume that nominators always | ||
| // have themselves assigned to the first validator in their list. | ||
| let chosen = if nominees.len() > 0 { | ||
| candidates[0] | ||
| } else { | ||
| continue | ||
| }; | ||
| if let Ok(index) = candidates.binary_search_by_key(&who, |i| &i.0) { | ||
| let stash_balance = Self::stash_balance(&who); | ||
| candidates[index].1.total += stash_balance; | ||
| candidates[index].1.others.push((who, stash_balance)); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -606,15 +573,19 @@ impl<T: Trait> Module<T> { | |
| } | ||
| } | ||
|
|
||
| // Get the new staker set by sorting by total backing stake and truncating. | ||
| candidates.sort_unstable_by(|(a, b)| a.1.total > b.1.total); | ||
| candidates.truncate(Self::validator_count() as usize); | ||
|
|
||
| // Figure out the minimum stake behind a slot. | ||
| let slot_stake = candidates.last().map(|i| i.1.total).unwrap_or_default(); | ||
| <SlotStake<T>>::put(&slot_stake); | ||
|
|
||
| // Populate Stakers. | ||
| for (who, exposure) in &candidates { | ||
| <Stakers<T>>::insert(who, exposure); | ||
| } | ||
| // Set the new validator set. | ||
| <session::Module<T>>::set_validators( | ||
| candidates.into_iter().map(|i| i.0).collect::<Vec<_>>() | ||
| ); | ||
|
|
@@ -657,50 +628,17 @@ impl<T: Trait> Module<T> { | |
| }); | ||
| } | ||
|
|
||
| let event = if new_slash_count > grace { | ||
| let slash = { | ||
| let base_slash = Self::current_offline_slash(); | ||
| let instances = slash_count - grace; | ||
|
|
||
| let mut total_slash = BalanceOf::<T>::default(); | ||
| for i in instances..(instances + count as u32) { | ||
| if let Some(total) = base_slash.checked_shl(i) | ||
| .and_then(|slash| total_slash.checked_add(&slash)) { | ||
| total_slash = total; | ||
| } else { | ||
| // reset slash count only up to the current | ||
| // instance. the total slash overflows the unit for | ||
| // balance in the system therefore we can slash all | ||
| // the slashable balance for the account | ||
| <SlashCount<T>>::insert(v.clone(), slash_count + i); | ||
| total_slash = Self::slashable_balance(&v); | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| total_slash | ||
| }; | ||
| let prefs = Self::validators(&v); | ||
|
|
||
| let event = if new_slash_count > grace + prefs.unstake_threshold { | ||
| // They're bailing. | ||
| let slash = Self::current_offline_slash() | ||
| .checked_shl(prefs.unstake_threshold) | ||
shawntabrizi marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
shawntabrizi marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| .unwrap_or_else(Self::slot_stake); | ||
|
||
| let _ = Self::slash_validator(&v, slash); | ||
|
|
||
| let next_slash = match slash.checked_shl(1) { | ||
| Some(slash) => slash, | ||
| None => Self::slashable_balance(&v), | ||
| }; | ||
|
|
||
| let instances = new_slash_count - grace; | ||
| if instances > Self::validator_preferences(&v).unstake_threshold | ||
| || Self::slashable_balance(&v) < next_slash | ||
| || next_slash <= slash | ||
| { | ||
| if let Some(pos) = Self::intentions().into_iter().position(|x| &x == &v) { | ||
| Self::apply_unvalidate(&v, pos) | ||
| .expect("pos derived correctly from Self::intentions(); \ | ||
| apply_unvalidate can only fail if pos wrong; \ | ||
| Self::intentions() doesn't change; qed"); | ||
| } | ||
| let _ = Self::apply_force_new_era(false); | ||
| } | ||
| <Validators<T>>::remove(&v); | ||
| let _ = Self::apply_force_new_era(false); | ||
|
|
||
| RawEvent::OfflineSlash(v.clone(), slash) | ||
| } else { | ||
| RawEvent::OfflineWarning(v.clone(), slash_count) | ||
|
|
@@ -733,10 +671,10 @@ impl<T: Trait> EnsureAccountLiquid<T::AccountId> for Module<T> { | |
| impl<T: Trait> OnFreeBalanceZero<T::AccountId> for Module<T> { | ||
| fn on_free_balance_zero(who: &T::AccountId) { | ||
| if let Some(controller) = <Bonded<T>>::take(who) { | ||
| <Ledger<T>>::kill(&controller); | ||
| <ValidatorPreferences<T>>::remove(&controller); | ||
| <Ledger<T>>::remove(&controller); | ||
| <Validators<T>>::remove(&controller); | ||
| <SlashCount<T>>::remove(&controller); | ||
| <Desired<T>>::remove(&controller); | ||
| <Nominators<T>>::remove(&controller); | ||
| } | ||
| } | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.