pallet_session/
disabling.rs1use crate::*;
19use frame_support::defensive;
20pub trait DisablingStrategy<T: Config> {
22 fn decision(
24 offender_stash: &T::ValidatorId,
25 offender_slash_severity: OffenceSeverity,
26 currently_disabled: &Vec<(u32, OffenceSeverity)>,
27 ) -> DisablingDecision;
28}
29
30#[derive(Debug)]
36pub struct DisablingDecision {
37 pub disable: Option<u32>,
38 pub reenable: Option<u32>,
39}
40
41impl<T: Config> DisablingStrategy<T> for () {
42 fn decision(
43 _offender_stash: &T::ValidatorId,
44 _offender_slash_severity: OffenceSeverity,
45 _currently_disabled: &Vec<(u32, OffenceSeverity)>,
46 ) -> DisablingDecision {
47 DisablingDecision { disable: None, reenable: None }
48 }
49}
50fn factor_based_disable_limit(validators_len: usize, disabling_limit_factor: usize) -> usize {
57 validators_len
58 .saturating_sub(1)
59 .checked_div(disabling_limit_factor)
60 .unwrap_or_else(|| {
61 defensive!("DISABLING_LIMIT_FACTOR should not be 0");
62 0
63 })
64}
65
66pub struct UpToLimitDisablingStrategy<const DISABLING_LIMIT_FACTOR: usize = 3>;
73
74impl<const DISABLING_LIMIT_FACTOR: usize> UpToLimitDisablingStrategy<DISABLING_LIMIT_FACTOR> {
75 pub fn disable_limit(validators_len: usize) -> usize {
78 factor_based_disable_limit(validators_len, DISABLING_LIMIT_FACTOR)
79 }
80}
81
82impl<T: Config, const DISABLING_LIMIT_FACTOR: usize> DisablingStrategy<T>
83 for UpToLimitDisablingStrategy<DISABLING_LIMIT_FACTOR>
84{
85 fn decision(
86 offender_stash: &T::ValidatorId,
87 _offender_slash_severity: OffenceSeverity,
88 currently_disabled: &Vec<(u32, OffenceSeverity)>,
89 ) -> DisablingDecision {
90 let active_set = Validators::<T>::get();
91
92 if currently_disabled.len() >= Self::disable_limit(active_set.len()) {
94 log!(
95 debug,
96 "Won't disable: reached disabling limit {:?}",
97 Self::disable_limit(active_set.len())
98 );
99 return DisablingDecision { disable: None, reenable: None }
100 }
101
102 let offender_idx = if let Some(idx) = active_set.iter().position(|i| i == offender_stash) {
103 idx as u32
104 } else {
105 log!(debug, "Won't disable: offender not in active set",);
106 return DisablingDecision { disable: None, reenable: None }
107 };
108
109 log!(debug, "Will disable {:?}", offender_idx);
110
111 DisablingDecision { disable: Some(offender_idx), reenable: None }
112 }
113}
114
115pub struct UpToLimitWithReEnablingDisablingStrategy<const DISABLING_LIMIT_FACTOR: usize = 3>;
126
127impl<const DISABLING_LIMIT_FACTOR: usize>
128 UpToLimitWithReEnablingDisablingStrategy<DISABLING_LIMIT_FACTOR>
129{
130 pub fn disable_limit(validators_len: usize) -> usize {
133 factor_based_disable_limit(validators_len, DISABLING_LIMIT_FACTOR)
134 }
135}
136
137impl<T: Config, const DISABLING_LIMIT_FACTOR: usize> DisablingStrategy<T>
138 for UpToLimitWithReEnablingDisablingStrategy<DISABLING_LIMIT_FACTOR>
139{
140 fn decision(
141 offender_stash: &T::ValidatorId,
142 offender_slash_severity: OffenceSeverity,
143 currently_disabled: &Vec<(u32, OffenceSeverity)>,
144 ) -> DisablingDecision {
145 let active_set = Validators::<T>::get();
146
147 let offender_idx = if let Some(idx) = active_set.iter().position(|i| i == offender_stash) {
149 idx as u32
150 } else {
151 log!(debug, "Won't disable: offender not in active set",);
152 return DisablingDecision { disable: None, reenable: None }
153 };
154
155 if let Some((_, old_severity)) =
157 currently_disabled.iter().find(|(idx, _)| *idx == offender_idx)
158 {
159 if offender_slash_severity > *old_severity {
160 log!(debug, "Offender already disabled but with lower severity, will disable again to refresh severity of {:?}", offender_idx);
161 return DisablingDecision { disable: Some(offender_idx), reenable: None };
162 } else {
163 log!(debug, "Offender already disabled with higher or equal severity");
164 return DisablingDecision { disable: None, reenable: None };
165 }
166 }
167
168 if currently_disabled.len() >= Self::disable_limit(active_set.len()) {
171 log!(
172 debug,
173 "Reached disabling limit {:?}, checking for re-enabling",
174 Self::disable_limit(active_set.len())
175 );
176
177 if let Some((smallest_idx, _)) = currently_disabled
180 .iter()
181 .filter(|(_, severity)| *severity <= offender_slash_severity)
182 .min_by_key(|(_, severity)| *severity)
183 {
184 log!(debug, "Will disable {:?} and re-enable {:?}", offender_idx, smallest_idx);
185 return DisablingDecision {
186 disable: Some(offender_idx),
187 reenable: Some(*smallest_idx),
188 }
189 } else {
190 log!(debug, "No smaller offender found to re-enable");
191 return DisablingDecision { disable: None, reenable: None }
192 }
193 } else {
194 log!(debug, "Will disable {:?}", offender_idx);
196 return DisablingDecision { disable: Some(offender_idx), reenable: None }
197 }
198 }
199}